InnoDB行锁算法

行锁算法

InnodB行锁主要有以下3种实现算法:

Record Lock:单个行记录上的锁,总是按索引键锁定记录。

Gap Lock:间隙锁,锁定一个范围,但不包含记录本身。

Next-Key Lock:Record Lock+ Gap Lock,锁定一个范围,并且锁定记录本身。

其中,Next-Key Lock是结合了Record Lock和Gap Lock的一种锁定算法。默认隔离级别REPEATABLE-READ下,InnoDB中行锁默认使用算法Next-Key Lock,只有当查询的索引是唯一索引或主键时,InnoDB会对Next-Key Lock进行优化,将其降级为Record Lock,即仅锁住索引本身,而不是范围。当查询的索引为辅助索引时,InnoDB则会使用Next-Key Lock进行加锁。

划分间隙

action_temp表中id为主键,action_type有辅助索引idx_action_type。

idx_action_type索引键值区间可表示为:

(-∞,2)

(2,4)

(4,5)

(5,11)

(11, 14)

(14, 16)

(16, 18)

(18, 22)

(22, 24)

(24, +∞)

只要这些区间对应的两个临界记录中间可以插入记录,就认为区间对应的记录之间有间隙。如:区间(2,4),可以插入action_type为3的记录,这就认为该区间有间隙。

示例

我们首先分析行锁算法对insert的影响,在两个Session中分别开启事务:

timeSession ASession B
1beginbegin
2select * from action_temp where action_type=4 for update; 
3 insert into action_temp (id,action_type) values(2,3);--阻塞

当在sessionA中执行select id,type from action_temp  where action_type=4 for update时,根据Next-Key Lock的加锁规则,Innodb会使用Gap Lock锁定区间(2,4),(4,5),同时使用Record Lock锁定辅助索引键值4,最终锁定区间(2,5)。因此,在sessionB中执行以下insert语句都将导致阻塞:

1.insert into action_temp (id,action_type) values(2,2);
2.insert into action_temp (id,action_type) values(2,3);
3.insert into action_temp (id,action_type) values(2,4);
4.insert into action_temp (id,action_type) values(4,4);
5.insert into action_temp (id,action_type) values(5,4);
6.insert into action_temp (id,action_type) values(4,5);
7.insert into action_temp (id,action_type) values(5,5);

能够正常插入的insert语句:

1.insert into action_temp (id,action_type) values(7,5);
2.insert into action_temp (id,action_type) values(9,5);

辅助索引键值2,5实际并没有被锁定,为何执行语句1,6,7也会阻塞?

我们知道间隙锁会锁定辅助索引区间(2,5)内的间隙,而不会锁定边界值。但是,当我们在进行边界值的操作时,Innodb为了避免影响到间隙锁,当插入记录的辅助索引键值为下界2时,要求插入记录的主键键值必须小于下界对应的主键键值1。同时,当插入记录的辅助索引键值为上界5时,要求插入记录的主键键值必须大于上界对应的主键键值6。这就是执行语句1,6,7都会阻塞的原因,而语句8,9显然主键键值大于上界记录对应的主键键值6。再看一个示例:

timeSession ASession B
1beginbegin
2select * from action_temp where action_type=15 for update; 
3 --阻塞
insert into action_temp (id,action_type) values(22,14);
insert into action_temp (id,action_type) values(14,18);

根据Next-Key Lock的加锁规则,Innodb会使用Gap Lock锁定区间(14, 16),(16, 18),同时使用Record Lock锁定辅助索引键值16,最终锁定区间(14,18)。SessionB中,当action_type为下界键值14时,若主键值大于21,insert语句就会阻塞,当action_type为上界键值18时,若主键值小于15,insert语句就会阻塞。此例主要是为了说明上界键值的主键比下界键值的主键小的情况。

下面我们看看执行update语句的锁定情况:

由于Innodb使用Record Lock锁定了action_type=4的记录,因此在Session执行带where条件action_type=4的update语句都会阻塞。除此之外,还会阻塞的update语句为:

1.update action_temp set action_type=3 where action_type=2;
2.update action_temp set action_type=3 where action_type=5;
3.update action_temp set action_type=5 where action_type=2;
4.update action_temp set action_type=4 where action_type=2;
5.update action_temp set action_type=4 where action_type=5;
6.update action_temp set action_type=2 where action_type=5;

不会阻塞的语句:

1.update action_temp set action_type=3 where action_type=3;
2.update action_temp set action_type=4 where action_type=3;

总结:InnoDB使用Next-Key Lock对辅助索引进行加锁后,除了阻塞where条件action_type=4的update语句外,当where条件action_type的值为锁定区间的上下界时,set字句中若包含action_type,则action_type的值不能包含被锁定区间的任意值,包含上下界;当where条件action_type的值为间隙内的值,无论set字句是否包含action_type,都不会阻塞,因为间隙内的键值对应的记录根本不存在,即使成功执行,也不会影响间隙锁的锁定。

对delete语句的锁定情况: delete from action_temp where action_type=4 ;

总结:执行delete语句时,只会阻塞被Record Lock锁定的action_type=4的记录。

当辅助索引键值不存在时,InnoDB将如何锁定呢?

timeSession ASession B
1beginbegin
2select * from action_temp where action_type=3 for update; 
3 将阻塞的语句:
insert into action_temp (id,action_type) values(2,3);
insert into action_temp (id,action_type) values(2,3);
insert into action_temp (id,action_type) values(2,4);

从上面示例即可知,当键值不存在时,InnoDB将以第一个比给定键值小的值为下界,以第一个比给定键值大的值为上界,锁住整个区间内的数据,但不包含上下界,即sessionA锁定的区间为(2,4)。

当在辅助索引上使用范围条件查询时,InnoDB将如何锁定?

timeSession ASession B
1beginbegin
2select * from action_temp where action_type>4 for update; 
3 将阻塞的语句:
insert into amg_order(id,type) values(7,5);
......

此时,InnoDB同样会扫描以第一个比键值比给定键值小的值为下界 ,通时将action_type>3的所有记录(无论是否存在)锁定,任何insert type>3的语句都会阻塞,即sessionA锁定的范围为(2,+∞)

那么,一般地,当InnoDB对辅助索引加行锁时,默认隔离级别REPEATABLE-READ下,使用Next-Key Lock算法,InnoDB将以第一个比给定键值小的值为下界(不包含),以第一个比给定键值大的值为上界(不包含),锁住整个区间内的数据。

对于Insert语句:

  1. 若Insert语句包含该区间内键值(无论是否存在)都将导致阻塞
  2. 若Insert语句包含区间下界值时,待插入记录的主键必须小于下界记录对应的主键值
  3. 若Insert语句包含区间上界值时,待插入记录的主键必须大于上界记录对应的主键值

对于update语句:

  1. 若update语句where条件包含给定键值,将阻塞
  2. 若update语句where条件包含上下界键值,set字句不能包含被锁定区间的任意值,包含上下界

对于delete语句:delete操作只会阻塞在键值上。

注意:对于多列的唯一索引,当查询仅包含索引列中部分列,InnoDB同样使用使用Next-Key Lock进行加锁,而不会降级为Record Lock。

InnoDB使用间隙锁的目的,一方面是为了防止幻读,以满足相关隔离级别的要求,另外一方面,是为了满足其恢复和复制的需要。

很显然,InnoDB这种加锁机制会阻塞符合条件范围内键值的并发插入,这往往会造成严重的锁等待。当然,用户可使用下面两种方式显示地关闭间隙锁(Gap Lock):

1 将事务的隔离级别设为READ COMMITTED

2将参数innodb_locks_unsafe_for_binlog设置为1(目前已被弃用)

注意:方式1显然破坏了事务的隔离性,会导致幻读的发生;方式2在replication中可能导致主从数据不一致。

欢迎指出本文有误的地方,转载请注明原文出处https://my.oschina.net/7001/blog/880204

转载于:https://my.oschina.net/7001/blog/880204

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值