ToB企服应用市场:ToB评测及商务社交产业平台

标题: MySQL——后码锁(Next-Key Block) [打印本页]

作者: 卖不甜枣    时间: 2023-9-2 22:09
标题: MySQL——后码锁(Next-Key Block)
众所周知,Mysql的事务隔离级别分为4个,分别是READ-UNCOMMITED,READ-COMMITED,REPEATABLE-READ,SERIALIZABLE,在常规数据库概论中,前三种事务隔离级别会带来脏读、不可重复读、幻读的问题,对应关系如下:
脏读不可重复读幻读READ-UNCOMMITED√√√READ-COMMITED×√√REPEATABLE-READ××√SERIALIZABLE×××但是在Mysql中使用了Next-key Block解决了幻读问题,下面我们通过讨论该问题来详细讨论Next-key Block,这里考虑一个常见的幻读情况,首先创建示例表:
  1. create database test;
  2. use test;
  3. CREATE TABLE `t` (
  4.   `t1` int(11) NOT NULL,
  5.   `t2` int(11) DEFAULT NULL,
  6.   PRIMARY KEY (`t1`),
  7.   KEY `t2` (`t2`)
  8. ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
复制代码
将其中加入几条示例数据:
  1. insert into t values(1,0),(2,10),(3,20),(4,30),(5,40);
复制代码
接下来考虑一个常见的幻读情况,我们可以先将mysql的Next-key Block关闭,可以采用如下两种方式对其进行关闭:
由于innodb_locks_unsafe_for_binlog参数需要重启服务器才能进行配置,因此我们采用第一种方式,将session的事务隔离级别设置为READ-COMMITTED。下面考察一般的幻读情况,我们的实验方式如下:
事务1事务2begin;select * from t where t2=20;(查到一条记录,(3,20))begin;insert into t value(6,20);commit;select * from t where t2=20;(查到两条记录(3,20),(6,20))commit;事务1实验过程如下:
  1. mysql> set session transaction isolation level read committed; # 设置当前session的事务隔离级别为READ-COMMITED
  2. Query OK, 0 rows affected (0.00 sec)
  3. mysql> set autocommit = 0; # 取消自动Commit
  4. Query OK, 0 rows affected (0.00 sec)
  5. mysql> begin; # 开始一个新事务
  6. Query OK, 0 rows affected (0.00 sec)
  7. mysql> select * from t where t2=20;  # 首次查询t2为20的数据,查询点1
  8. +----+------+
  9. | t1 | t2   |
  10. +----+------+
  11. |  3 |   20 |
  12. +----+------+
  13. 1 row in set (0.00 sec)
  14. mysql> select * from t where t2=20; # 事务2未提交时查询t2为20的数据,查询点2
  15. +----+------+
  16. | t1 | t2   |
  17. +----+------+
  18. |  3 |   20 |
  19. +----+------+
  20. 1 row in set (0.00 sec)
  21. mysql> select * from t where t2=20; # 事务2提交后查询t2为20的数据,查询点3(出现幻读)
  22. +----+------+
  23. | t1 | t2   |
  24. +----+------+
  25. |  3 |   20 |
  26. |  6 |   20 |
  27. +----+------+
  28. 2 rows in set (0.00 sec)
  29. mysql> commit; # 提交事务1
  30. Query OK, 0 rows affected (0.00 sec)
复制代码
事务2执行过程如下:
  1. mysql> set session transaction isolation level read committed; # 设置当前session的事务隔离级别为READ-COMMITED
  2. Query OK, 0 rows affected (0.00 sec)
  3. mysql> set autocommit = 0; # 取消自动Commit
  4. Query OK, 0 rows affected (0.00 sec)
  5. mysql> begin; # 开始一个事务
  6. Query OK, 0 rows affected (0.00 sec)
  7. mysql> insert into t value(6,20); # 调用点1、调用点2之间进行插入新数据  这里同时也是为了营造t2列的索引是非唯一索引的情况,否则会简化为Record Lock,为下一步的讨论做准备
  8. Query OK, 1 row affected (0.00 sec)
  9. mysql> commit; # 调用点2、调用点3之间进行提交
  10. Query OK, 0 rows affected (0.00 sec)
复制代码
可以看到,这种情况下幻读正常发生。
接下来,考察使用Next-key Block防止出现幻读的情况时,会发生的情况。这里我们再次强调一下我对幻读的理解,考虑当前有事务A、B,事务A中具有两条一模一样的查询语句执行(例如上述例子的调用点1和3,注意,我们不考虑调用点2),在两条查询语句执行的中间,事务B提交了会影响到事务A两条查询语句结果的插入请求(事务2的插入语句),这时,事务A的查询语句的执行结果会和第一条的查询结果不同,就好似出现了幻觉。那么接下来真正开始讨论Next-key Block。
Next key Block

讨论Next-key Block之前,我们需要对一些基本概念进行解释,Mysql的锁算法有3种:
介绍完基础概念之后我们继续开始探究,基本的查询语句显而易见有3种,大于、小于、等于、不等于,这里我们主要讨论这四种情况,接下来对其进行一一讨论,不过首先要都把事务隔离级别设置为REPEATABLE-READ。
1. 大于的情况

考虑查询语句更改为如下语句:
  1. select * from t where t2>20 for update;
复制代码
在这种情况下,我们猜想应该给大于20的t2列的索引全部加锁,而对于插入的方面又可以分为3类:
2.小于的情况

考虑查询语句更改为如下语句:
[code]select * from t where t2A的查询同时,另一条事务插入=A的数据时都会加锁,而且加锁类型也相同。</p>在进行>A的讨论中,事务1在进行select查询时,锁住了(A,+无穷)中的所有的索引,注意 这里锁住的是索引,即记录锁,不是间隙锁。结合上面讨论的例子,也就是进行>20的讨论时对30,40,无穷大进行了加锁,由于使用的是select ... for update因此加的是X锁,当进行插入数据的时候,例如插入t2=20的数据时,查找下一个索引即t2=30的索引,发现其被锁住了,因此无法插入。插入>20的数据时同理。

在进行




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4