mysql8 Record Locks ,Gap Locks, Next-Key Locks 实验1
mysql8 Record Locks ,Gap Locks, Next-Key Locks 实验3
上文讨论了 RR 隔离级别 主键索引的加锁情况,
本文接着讨论 RR 隔离级别下 二级唯一索引和普通二级索引 以及无索引时的加锁情况。
4.3 二级唯一索引等值查询
这次我们对 f1 字段操作,这是一个二级 唯一索引字段
4.3.1 锁定存在的值
-- 查询存在的值 11
START TRANSACTION;
SELECT PS_CURRENT_THREAD_ID() THREAD_ID ,t.* FROM locktest t WHERE t.f1=11 FOR UPDATE ;
-- 当前线程THREAD_ID=44
+-----------+----+----+----+----+
| THREAD_ID | id | f1 | f2 | f3 |
+-----------+----+----+----+----+
| 44 | 11 | 11 | 11 | 11 |
+-----------+----+----+----+----+
1 row in set (0.0 sec)
查询锁的信息,发现有3个锁,如下图所示:
【行号1】 是一个表级的意向锁
【行号2】 是一个加在 二级唯一索引 ui_f1上的 record 锁,锁类型为 X(排他),REC_NO__GAP 说明没有对间隙加锁
值得注意的是 LOCK_DATA为【11,11】 ,我们知道二级索引的record 组成为(key,主键),所以第一个11 是二级索引字段f1 的值,而第二个11 是主键的值
【行号3】 是一个主键索引上的 排他record 锁
4.3.2 锁定不存在的值
-- 查询存在的值 12
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT PS_CURRENT_THREAD_ID() THREAD_ID ,t.* FROM locktest t WHERE t.f1=12 FOR UPDATE;
Empty set (0.00 sec)
查询加锁情况:
【行2】 对ui_f1 在数据(13,13)上添加了 X,GAP 锁,这其实就是一个 record lock和gap lock 的组合
这是个 next key lock,锁定的范围应该是 (11,13]
我们再开启一个连接验证一下锁定的范围:
-- 插入9 ok
mysql> INSERT INTO locktest (id,f1,f2,f3) VALUES (9,9,9,9);
Query OK, 1 row affected (0.00 sec)
-- 插入12 阻塞
INSERT INTO locktest (id,f1,f2,f3) VALUES (12,12,12,12);
-- 插入 14 ok
mysql> INSERT INTO locktest (id,f1,f2,f3) VALUES (14,14,14,14);
Query OK, 1 row affected (0.00 sec)
4.4 二级唯一索引范围查询
验证完唯一索引的等值查询加锁情况,我们开始验证范围查询
4.4.1 单范围查询
START TRANSACTION;
mysql> SELECT PS_CURRENT_THREAD_ID() THREAD_ID ,t.* FROM locktest t WHERE t.f1>11 FOR UPDATE;
+-----------+----+----+----+----+
| THREAD_ID | id | f1 | f2 | f3 |
+-----------+----+----+----+----+
| 44 | 13 | 13 | 13 | 13 |
| 44 | 20 | 20 | 20 | 20 |
+-----------+----+----+----+----+
2 rows in set (0.00 sec)
加锁信息如下:
4.4.2 双范围查询
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT PS_CURRENT_THREAD_ID() THREAD_ID ,t.* FROM locktest t WHERE t.f1>11 AND t.f1<20 FOR UPDATE;
+-----------+----+----+----+----+
| THREAD_ID | id | f1 | f2 | f3 |
+-----------+----+----+----+----+
| 44 | 13 | 13 | 13 | 13 |
+-----------+----+----+----+----+
1 row in set (0.00 sec)
加锁信息如下图:
4.4.3 不存在的双范围查询
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql> -- 记录 12 和21 都不存在
mysql> SELECT * FROM locktest t WHERE t.f1>=12 AND t.f1<=21 FOR UPDATE;
+----+----+----+----+
| id | f1 | f2 | f3 |
+----+----+----+----+
| 13 | 13 | 13 | 13 |
| 20 | 20 | 20 | 20 |
+----+----+----+----+
2 rows in set (0.00 sec)
加锁信息如下:
4.5 非唯一索引等值查询
这次我们对 f2 字段操作,这个一个二级非唯一索引字段
4.5.1 锁定存在的值
-- 查询存在的值 11
START TRANSACTION;
SELECT PS_CURRENT_THREAD_ID() THREAD_ID ,t.* FROM locktest t WHERE t.f2=11 FOR UPDATE ;
-- 当前线程THREAD_ID=44
+-----------+----+----+----+----+
| THREAD_ID | id | f1 | f2 | f3 |
+-----------+----+----+----+----+
| 44 | 11 | 11 | 11 | 11 |
+-----------+----+----+----+----+
1 row in set (0.0 sec)
查询锁的信息,发现有4个锁,如下图所示:
【行号1】 是一个表级的意向锁
【行号2】 是一个加在 二级索引 i_f2 上的 record 锁,锁类型为 X(排他), 值得注意的是 LOCK_DATA为【11,11】 ,我们知道二级索引的record 组成为(key,主键),所以第一个11 是二级索引字段f1 的值,而第二个11 是主键的值
【行号3】 是一个主键索引上的 排他record 锁
【行号4】 是一个 next key lock,因为 这是一个组合锁(X,GAP),锁定的数据为(13,13),也因为 13 是我们查询条件11 的下一个key
综上来看,加锁的范围是 (11,13]
也就是说对于二级非唯一索引的等值查询也会产生 next key lock
我们用以下sql 验证以下加锁范围
-- 启动一个新窗口执行以下操作
-- 插入 <11 的数据
mysql> INSERT INTO locktest (id,f1,f2,f3) VALUES (9,9,9,9);
Query OK, 1 row affected (0.00 sec)
-- 插入 =11 的数据 ,阻塞
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO locktest (id,f1,f2,f3) VALUES (111,111,11,11);
注意 【行2】 的 X,GAP,INSERT_INTENTION 无法获取锁 正在WATING
-- 插入>11 的数据,阻塞
START TRANSACTION;
INSERT INTO locktest (id,f1,f2,f3) VALUES (12,12,12,12);
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ozkmj4KQ-1651735032664)(./assets/20220505141220-image.png)]
-- 插入 =13 的数据,不阻塞,这说明 在13 上的X,GAP 锁并不阻止 13 的插入
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO locktest (id,f1,f2,f3) VALUES (1313,1313,13,13);
Query OK, 1 row affected (0.00 sec)
-- 插入>13 的数据,不阻塞
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO locktest (id,f1,f2,f3) VALUES (14,14,14,14);
Query OK, 1 row affected (0.00 sec)
4.5.2 锁定不存在的值
-- 查询存在的值 12
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT PS_CURRENT_THREAD_ID() THREAD_ID ,t.* FROM locktest t WHERE t.f2=12 FOR UPDATE;
Empty set (0.00 sec)
查询加锁情况:
【行2】 对 i_f2 在数据(13,13)上添加了 X,GAP 锁,这其实就是一个 record lock和gap lock 的组合
这是个 next key lock,锁定的范围应该是 (11,13]
4.6 非唯一索引范围查询
验证完 非唯一索引的等值查询加锁情况,我们开始验证范围查询
4.6.1 单范围查询
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT PS_CURRENT_THREAD_ID() THREAD_ID ,t.* FROM locktest t WHERE t.f2>11 FOR UPDATE;
+-----------+----+----+----+----+
| THREAD_ID | id | f1 | f2 | f3 |
+-----------+----+----+----+----+
| 44 | 13 | 13 | 13 | 13 |
| 44 | 20 | 20 | 20 | 20 |
+-----------+----+----+----+----+
2 rows in set (0.00 sec)
加锁信息如下:
4.6.2 双范围查询
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT PS_CURRENT_THREAD_ID() THREAD_ID ,t.* FROM locktest t WHERE t.f2>11 AND t.f2<20 FOR UPDATE;
+-----------+----+----+----+----+
| THREAD_ID | id | f1 | f2 | f3 |
+-----------+----+----+----+----+
| 44 | 13 | 13 | 13 | 13 |
+-----------+----+----+----+----+
1 row in set (0.00 sec)
加锁信息如下图:
4.6.3 不存在的双范围查询
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql> -- 记录 12 和21 都不存在
mysql> SELECT * FROM locktest t WHERE t.f2>=12 AND t.f2<=21 FOR UPDATE;
+----+----+----+----+
| id | f1 | f2 | f3 |
+----+----+----+----+
| 13 | 13 | 13 | 13 |
| 20 | 20 | 20 | 20 |
+----+----+----+----+
2 rows in set (0.00 sec)
加锁信息如下:
4.7 无索引等值查询
这次我们对 f3 字段操作,这个一个 无索引字段
4.7.1 锁定存在的值
-- 查询存在的值 11
START TRANSACTION;
SELECT PS_CURRENT_THREAD_ID() THREAD_ID ,t.* FROM locktest t WHERE t.f3=11 FOR UPDATE ;
-- 当前线程THREAD_ID=44
+-----------+----+----+----+----+
| THREAD_ID | id | f1 | f2 | f3 |
+-----------+----+----+----+----+
| 44 | 11 | 11 | 11 | 11 |
+-----------+----+----+----+----+
1 row in set (0.0 sec)
查询锁的信息,发现数据库中的数据都添加了锁,如下图所示:
我们用以下sql 验证以下加锁范围
-- 启动一个新窗口执行以下操作
-- 插入 <11 的数据 阻塞 无法插入
mysql> INSERT INTO locktest (id,f1,f2,f3) VALUES (9,9,9,9);
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GLTiJz19-1651735032673)(./assets/20220505150509-image.png)]
注意 【行2】 的 X,GAP,INSERT_INTENTION 无法获取锁 正在WATING
-- 插入 =11 的数据 ,阻塞
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO locktest (id,f1,f2,f3) VALUES (111,111,11,11);
-- 插入>11 的数据,阻塞
START TRANSACTION;
INSERT INTO locktest (id,f1,f2,f3) VALUES (12,12,12,12);
-- 插入 =13 的数据,阻塞
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO locktest (id,f1,f2,f3) VALUES (1313,1313,13,13);
Query OK, 1 row affected (0.00 sec)
4.7.2 锁定不存在的值
-- 查询存在的值 12
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT PS_CURRENT_THREAD_ID() THREAD_ID ,t.* FROM locktest t WHERE t.f3=12 FOR UPDATE;
Empty set (0.00 sec)
查询加锁情况:
4.8 无索引范围查询
这里就不一一验证了,因为在无索引的情况下都是全表锁定。这也说明了加索引的重要性