InnoDB|带你认识什么是MySQL的内存架构和索引说明( 四 )


### Session A 执行更新语句
START TRANSACTION;
UPDATE t SET b = 5 WHERE b = 3;

### Session B 在sessionA之后执行
UPDATE t SET b = 4 WHERE b = 2;

在InnoDB执行每个更新的时候 , 它先为每一行获取一个排它锁 , 然后决定是否修改它 。 如果InnoDB不修改这行 , 它就会释放这个锁 。 否则InnoDB持有锁 , 直到事务结束 。
当使用默认的可重复读级别时 , 第一个UPDATE在它读取的每行上获取x锁 , 不释放它们中的任何一个:


x-lock(12); retain x-lock
x-lock(23); update(23) to (25); retain x-lock
x-lock(32); retain x-lock
x-lock(43); update(43) to (45); retain x-lock
x-lock(52); retain x-lock
第二个UPDATE在尝试获取任何锁的时候会立即阻塞(因为第一个更新在所有行上保留了锁) , 并且在第一次UPDATE提交或回滚之前不会继续

x-lock(12); block and wait for first UPDATE to commit or roll back
如果使用READ COMMITTED , 第一个UPDATE会在每行上获取一个x锁 , 并释放这些不修改的行

x-lock(12); unlock(12)
x-lock(23); update(23) to (25); retain x-lock
x-lock(32); unlock(32)
x-lock(43); update(43) to (45); retain x-lock
x-lock(52); unlock(52)
对于第二个UPDATE , InnoDB执行“半一致”读 , 将读取的每一行的最新版本返回给MySQL , 以便MySQL可以确定该行是否匹配UPDATE的WHERE条件

x-lock(12); update(12) to (14); retain x-lock
x-lock(23); unlock(23)
x-lock(32); update(32) to (34); retain x-lock
x-lock(43); unlock(43)
x-lock(52); update(52) to (54); retain x-lock
然后如果WHERE条件包含索引列 , 并且InnoDB使用这个索引 , 那么在获取和保留记录锁的时候只考虑索引列 。
在下面这个例子 , 第一个UPDATE在b=2的每一行上获取并持有x锁 , 第二个UPDATE当它尝试获取相同记录上的x锁的时候会阻塞 , 因为它也使用列b上定义的索引
CREATE TABLE t (a INT NOT NULL b INT c INT INDEX (b)) ENGINE = InnoDB;
INSERT INTO t VALUES (123)(224);
COMMIT;

### Session A
START TRANSACTION;
UPDATE t SET b = 3 WHERE b = 2 AND c = 3;

### Session B
UPDATE t SET b = 4 WHERE b = 2 AND c = 4;

  • READ UNCOMMITTED 读未提交
    SELECT语句以非阻塞的方式执行 , 但可能会使用行的早期版本 。 因此读是不一致的 , 也叫做脏读
  • SERIALIZABLE 串行化
    InnoDB会在autocommit禁用的时候隐式的转换所有普通的SELECT语句为SELECT … FOR SHARE 。 如果启用autocommit , 那么SELECT是它自己的事务 。 如果要强制普通的SELECT在其他事务修改了选中行的时候进行阻塞 , 需要禁用autocommit
autocommit Commit RollbackInnoDB中 , 所有用户活动都是在事务里 。 如果autocommit模式启用 , 每个SQL语句都会形成它自己的事务 。 默认MySQL为每个新连接启动session的时候 , 会设置autocommit为启用 , 所以MySQL会在每个SQL语句没有返回错误之后进行一次提交 。 如果语句返回错误 , 会依赖具体的错误进行提交或回滚
对于autocommit启用的session , 可以通过START TRANSACTION或BEGIN语句开始事务 , COMMIT或ROLLBACK结束事务 。
如果在一个session里通过SET autocommit = 0来禁用autocommit , 那么这个session会始终有一个打开的事务 。 COMMIT或ROLLBACK语句会结束当前事务 , 启动新的事务 。
如果session禁用了autocommit , 没有强制提交最后的事务 , 那么这事务会被回滚 。
一致非阻塞读一致读意味着InnoDB使用多版本控制呈现数据库在某个时间点的一个快照 。
查询可以看到在这个时间点之前其他事务已提交的更改 , 不能看得到之后或未提交的事务 。