Fanr

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

innodb事务锁

根据文档innodb锁分为以下几种:

  • 意向锁:

    就是简单的IX,IS这类意向锁,这个容易理解,比如要在表上读取一行记录,那么表可能会被加IS锁,在表上写入就会被加IX锁

  • 行锁:
    这个也非常容易理解,就是在行上面加锁,S锁或者X锁

  • gap锁:
    gap锁就是为了在repeatable read隔离级别下消除幻读引入的一种锁,打到serializable隔离级别的效果

  • next-key锁:
    其实这个锁是有gap锁和record锁组成的,LOCK_ORDINARY 表示next-key lock 但是在8.0中已经看不出来了。

  • 插入意向锁(INSERT_INTENTION)
    插入意向锁是gap锁的一种,2个插入意向所gap锁并不会产生冲突。

repeatable read 隔离级别

在这个隔离级别下,innodb为了实现repeatable read消除幻读,达到serializable的效果就引入了gap lock。

next-key lock和gap lock

LOCK_ORDINARY 表示next-key lock 但是在8.0中已经看不出来了,在performance_schema.data_locks中是gap锁和record2条记录:

 begin;
 update id = 45 where i=40;

在另外一个会话中执行:

 select thread_id,engine_transaction_id,object_name,index_name,lock_type,lock_mode,lock_status,lock_data from data_locks;
thread_id engine_transaction_id object_name index_name lock_type lock_mode lock_status lock_data
240089 5446 l NULL TABLE IX GRANTED NULL
240089 5446 l i RECORD X GRANTED 40, 50
240089 5446 l PRIMARY RECORD X,REC_NOT_GAP GRANTED 50
240089 5446 l i RECORD X,GAP GRANTED 60, 60
240089 5446 l i RECORD X,GAP GRANTED 40, 45

第2条和第5条组合其实就是一个next-key lock
第3条锁定primary 50 是因为原来的id=50

插入意向锁(INSERT_INTENTION lock)

在repeatable read隔离级别下,insert会先去判断一下 insert intention lock若成功,插入然后释放。且2个插入意向锁在同一个gap中并不会堵塞测试也很简单在上面事务的基础上执行语句:

insert into l values(60,35);

查看锁信息:

thread_id engine_transaction_id object_name index_name lock_type lock_mode lock_status lock_data
240289 5447 l NULL TABLE IX GRANTED NULL
240289 5447 l PRIMARY RECORD S,REC_NOT_GAP GRANTED 60
240289 5447 l i RECORD X,GAP,INSERT_INTENTION WAITING 40, 45
240089 5446 l NULL TABLE IX GRANTED NULL
240089 5446 l i RECORD X GRANTED 40, 50
240089 5446 l PRIMARY RECORD X,REC_NOT_GAP GRANTED 50
240089 5446 l i RECORD X,GAP GRANTED 60, 60
240089 5446 l i RECORD X,GAP GRANTED 40, 45

而执行成功后锁会变成:

thread_id engine_transaction_id object_name index_name lock_type lock_mode lock_status lock_data
240289 5454 l NULL TABLE IX GRANTED NULL

也就是说插入意向锁已经被释放。

在2个会话中分别执行,发现并不会堵塞执行成功:

 begin;
  insert into l values(52,51);
 begin;
 insert into l values(51,50);

锁信息:

thread_id engine_transaction_id object_name index_name lock_type lock_mode lock_status lock_data
240289 5454 l NULL TABLE IX GRANTED NULL
240089 5453 l NULL TABLE IX GRANTED NULL

read commit 隔离级别

因为innodb的特点,mysql的update并不会堵塞select语句。除非显示的提示加共享锁。
在read commit隔离级别下:

begin;
update l set id = 45 where i=40;
thread_id engine_transaction_id object_name index_name lock_type lock_mode lock_status lock_data
240089 5455 l NULL TABLE IX GRANTED NULL
240089 5455 l i RECORD X,REC_NOT_GAP GRANTED 40, 50
240089 5455 l PRIMARY RECORD X,REC_NOT_GAP GRANTED 50

和之前的相比上了lock_mode 少了带gap的标记,原先的x锁中也加了rec_not_gap。也就是说read commit不再锁定数据的gap。

堵塞判断

innodb堵塞判断因为有了gap,next-key,insert intention锁变的复杂(lock_rec_has_to_wait函数中体现):

  1. 首选判断意向锁,为此还定义了一个堵塞矩阵:

 static const byte lock_compatibility_matrix[5][5] = { 
    /**         IS        IX         S          X           AI */ 
    /* IS */ { TRUE, TRUE, TRUE, FALSE, TRUE}, 
    /* IX */ { TRUE, TRUE, FALSE, FALSE, TRUE}, 
    /* S */ { TRUE, FALSE, TRUE, FALSE, FALSE}, 
    /* X */ { FALSE, FALSE, FALSE, FALSE, FALSE}, 
    /* AI */ { TRUE, TRUE, FALSE, FALSE, FALSE} };
  1. 即使步骤1冲突,根据不同的lock_mode 开始判断,以下情况不需要等待:

    • 如果请求锁住的是 supremum 或者 LOCK_GAP 为 1 并且 LOCK_INSERT_INTENTION 为 0
    • 如果请求锁 LOCK_INSERT_INTENTION 为 0 并且已有锁是 LOCK_GAP 为 1
    • 如果请求锁 LOCK_GAP 为 1,请求锁 LOCK_REC_NOT_GAP 为 1
    • 如果已有锁 LOCK_INSERT_INTENTION 为 1

参考:

MySQL · 引擎特性 · InnoDB 事务锁系统简介
MySQL · 引擎特性 · Innodb 锁子系统浅析

posted on 2020-08-13 14:10  Fanr_Zh  阅读(293)  评论(1编辑  收藏  举报