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


Next-Key锁是索引记录上的记录锁和索引记录之前间隙上的间隙锁的组合
如果一个session在索引的记录R上有一个共享或排他锁 , 则另一个session不能在索引顺序中紧靠R之前的间隙中立即插入新的索引记录
假设索引包含值10 , 11 , 13 , 20 。 那么这个索引上可能的next-key锁包含下面的几种间隔
(negative infinity 10

(10 11

(11 13

(13 20

(20 positive infinity)

默认InnoDB运行在REPEATABLE READ事务隔离级别 。 这种情况下 , InnoDB使用next-key锁来进行搜索和索引扫描
插入意向锁 Insert Intention Locks行插入之前通过INSERT操作设置的一种间隙锁 。 此锁表示插入的意图 , 如果插入到同一个索引间隙的多个事务不在间隙的同个位置插入 , 则它们无需互相等待
假设有索引记录值4和7 。 不同的事务尝试插入5和6 , 在获得插入行的排它锁之前 , 每个事务都会使用插入意向锁锁住4和7之间的间隙 , 但因为行不冲突而不会互相阻塞
e.g. 客户端A创建一个表 , 包含了两个索引记录90和102 , 然后启动一个事务 , 对ID大于100的索引记录进行排它锁 。 这个排它锁包含了记录102前面的间隙
mysql> CREATE TABLE child (id int(11) NOT NULL PRIMARY KEY(id)) ENGINE=InnoDB;
mysql> INSERT INTO child (id) values (90)(102);

mysql> START TRANSACTION;
mysql> SELECT * FROM child WHERE id > 100 FOR UPDATE;
+-----+
| id  |
+-----+
| 102 |
+-----+

客户端B开始一个插入记录到间隙的事务 。 这个事务在它等待获取排它锁时接受插入意向锁
mysql> START TRANSACTION;
mysql> INSERT INTO child (id) VALUES (101);

AUTO-INC 锁是一种特殊的表锁 , 事务插入有AUTO_INCREMENT列的表的时候使用 。 如果一个事务正在向表插入值 , 任何其他事务在插入这个表的时候都必须等待 , 以便第一个事务接收连续的主键值
InnoDB事务模型事务隔离级别

  • REPEATABLE READ (默认级别)
    相同事务里的一致性读 , 使用第一次读的时候建立的快照 。 意味着在同一个事务里 , 如果发出了几个普通(非阻塞)的SELECT语句 , 这些SELECT语句彼此之间也是一致的
    对于锁定读(SELECT带有FOR UPDATE或FOR SHARE) , UPDATE DELETE语句 , 锁定依赖于语句是否在唯一搜索条件里使用了唯一索引 , 或者范围搜索条件
    • 对于唯一搜索条件的唯一索引 , InnoDB只锁定找到的索引记录 , 不包含它前面的间隙
    • 对于其他搜索条件 , InnoDB锁定索引扫描的范围 , 使用间隙锁或next-key锁阻止其他session插入间隙所覆盖的范围 。
  • READ COMMITTED
    即使在同一个事务里 , 每次一致性读都会设置并读取自己的新快照 。
    对于锁定读 , InnoDB只锁定索引记录 , 不包含他们之前的间隙 , 因此允许在锁定记录旁自由插入新记录
    因为禁用了间隙锁定 , 可能会出现不可重复读 , 因为其他session可以插入新行到间隙中
    影响:
    • 对于UPDATE或DELETE语句 , InnoDB只对它更新或删除的行持有锁 。 在MYSQL评估WHERE条件后 , 将释放不匹配行的锁 , 这大大降低了死锁的概率
    • 对于UPDATE语句 , 如果一个行已经锁定 , InnoDB执行“半一致”读 , 将最新提交的版本返回给MySQL , 以便MySQL可以决定该行是否匹配更新的WHERE条件
e.g. 可重复读 VS. 读已提交
CREATE TABLE t (a INT NOT NULL b INT) ENGINE = InnoDB;
INSERT INTO t VALUES (12)(23)(32)(43)(52);
COMMIT;

这种情况下 , 表没有索引 , 因此搜索和索引扫描时使用隐式的聚簇索引来进行记录锁定