隔離級(jí)別
MySQL默認(rèn)隔離級(jí)別為 REPEATABLE READ
常用的隔離級(jí)別為如下倆個(gè):
- REPEATABLE READ 可重復(fù)讀
- READ COMMITTED 已提交讀
間隙鎖(REPEATABLE READ)
官方文檔:https://dev.mysql.com/doc/refman/5.7/en/innodb-locking.html#innodb-gap-locks
- 間隙鎖定是對(duì)索引記錄之間的間隙的鎖定疹娶,或者是對(duì)第一個(gè)或最后一個(gè)索引記錄之前的間隙的鎖定净响。
- 對(duì)于唯一索引來說,沒有間隙鎖(不包括搜索條件含多列唯一索引情況)。
- 間隙鎖在 REPEATABLE READ 以上模式下才存在
實(shí)例展示
測(cè)試數(shù)據(jù)
環(huán)境:MySQL 8.0.18
【session1】
create database locktest;
drop table yqlock1;
create table yqlock1 (id int auto_increment primary key , a int ,b varchar(30),key idx_a(a));
insert into yqlock1(a,b) values (3,5),(5,12),(9,8),(7,1),(8,5),(15,20);
root@yq [locktest]> select * from yqlock1 order by a;
+----+------+------+
| id | a | b |
+----+------+------+
| 1 | 3 | 5 |
| 2 | 5 | 12 |
| 4 | 7 | 1 |
| 5 | 8 | 5 |
| 3 | 9 | 8 |
| 6 | 15 | 20 |
+----+------+------+
6 rows in set (0.00 sec)
RR隔離級(jí)別
條件字段有索引
# 查看隔離級(jí)別
root@yq [locktest]> show variables like '%iso%';
+-----------------------+-----------------+
| Variable_name | Value |
+-----------------------+-----------------+
| transaction_isolation | REPEATABLE-READ |
+-----------------------+-----------------+
1 row in set (0.01 sec)
begin;
update yqlock1 set b = '隔壁老王' where a = '5'; # a列存在索引
# 8.0 可以查看data_locks表查看鎖定的數(shù)據(jù)行
# 可以看到有一個(gè)間隙鎖 7, 4
root@yq [locktest]> select ENGINE,ENGINE_TRANSACTION_ID,THREAD_ID,EVENT_ID,OBJECT_SCHEMA,OBJECT_NAME,INDEX_NAME,LOCK_TYPE,LOCK_MODE,LOCK_STATUS,LOCK_DATA from performance_schema.data_locks;
+--------+-----------------------+-----------+----------+---------------+-------------+------------+-----------+---------------+-------------+-----------+
| ENGINE | ENGINE_TRANSACTION_ID | THREAD_ID | EVENT_ID | OBJECT_SCHEMA | OBJECT_NAME | INDEX_NAME | LOCK_TYPE | LOCK_MODE | LOCK_STATUS | LOCK_DATA |
+--------+-----------------------+-----------+----------+---------------+-------------+------------+-----------+---------------+-------------+-----------+
| INNODB | 2287449 | 155509 | 15 | locktest | yqlock1 | NULL | TABLE | IX | GRANTED | NULL |
| INNODB | 2287449 | 155509 | 15 | locktest | yqlock1 | idx_a | RECORD | X | GRANTED | 5, 2 |
| INNODB | 2287449 | 155509 | 15 | locktest | yqlock1 | PRIMARY | RECORD | X,REC_NOT_GAP | GRANTED | 2 |
| INNODB | 2287449 | 155509 | 15 | locktest | yqlock1 | idx_a | RECORD | X,GAP | GRANTED | 7, 4 |
+--------+-----------------------+-----------+----------+---------------+-------------+------------+-----------+---------------+-------------+-----------+
4 rows in set (0.00 sec)
# 【session2】新開一個(gè)窗口角钩,執(zhí)行insert
root@yq [locktest]> begin;
Query OK, 0 rows affected (0.00 sec)
root@yq [locktest]> insert into yqlock1 select 7,'5','aaa'; # 處于等待狀態(tài)
root@yq [locktest]> insert into yqlock1 select 7,'6','aaa'; # 處于等待狀態(tài)
root@yq [locktest]> insert into yqlock1 select 7,'7','aaa'; # 成功插入
Query OK, 1 row affected (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 0
在測(cè)試下更新的數(shù)據(jù)是不存在的記錄
【session1】
root@yq [locktest]> begin;
Query OK, 0 rows affected (0.00 sec)
root@yq [locktest]> update yqlock1 set b = '隔壁老王' where a = '12';
Query OK, 0 rows affected (0.00 sec)
Rows matched: 0 Changed: 0 Warnings: 0
root@yq [locktest]> select ENGINE,ENGINE_TRANSACTION_ID,THREAD_ID,EVENT_ID,OBJECT_SCHEMA,OBJECT_NAME,INDEX_NAME,LOCK_TYPE,LOCK_MODE,LOCK_STATUS,LOCK_DATA from performance_schema.data_locks;
+--------+-----------------------+-----------+----------+---------------+-------------+------------+-----------+-----------+-------------+-----------+
| ENGINE | ENGINE_TRANSACTION_ID | THREAD_ID | EVENT_ID | OBJECT_SCHEMA | OBJECT_NAME | INDEX_NAME | LOCK_TYPE | LOCK_MODE | LOCK_STATUS | LOCK_DATA |
+--------+-----------------------+-----------+----------+---------------+-------------+------------+-----------+-----------+-------------+-----------+
| INNODB | 2287472 | 155663 | 25 | locktest | yqlock1 | NULL | TABLE | IX | GRANTED | NULL |
| INNODB | 2287472 | 155663 | 25 | locktest | yqlock1 | idx_a | RECORD | X,GAP | GRANTED | 15, 6 |
+--------+-----------------------+-----------+----------+---------------+-------------+------------+-----------+-----------+-------------+-----------+
2 rows in set (0.00 sec)
【session2】
root@yq [locktest]> begin;
Query OK, 0 rows affected (0.00 sec)
root@yq [locktest]> insert into yqlock1 select 7,'9','aaa'; ## 等待
root@yq [locktest]> insert into yqlock1 select 8,'10','aaa'; ## 等待
root@yq [locktest]> insert into yqlock1 select 9,'12','aaa'; ## 等待
root@yq [locktest]> insert into yqlock1 select 10,'15','aaa';
Query OK, 1 row affected (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 0
由上示例可得知:
- RR模式下存在間隙鎖(GAP),會(huì)鎖住兩索引間的間隙阀参。
- 條件有索引情況下鸣皂,實(shí)際鎖住的數(shù)據(jù)是左閉右開(5,7],其實(shí)這是 next-key lock(行鎖+GAP)
條件字段無索引
【session1】
root@yq [locktest]> select * from yqlock1 order by b;
+----+------+------+
| id | a | b |
+----+------+------+
| 4 | 7 | 1 |
| 2 | 5 | 12 |
| 6 | 15 | 20 |
| 1 | 3 | 5 |
| 5 | 8 | 5 |
| 3 | 9 | 8 |
+----+------+------+
6 rows in set (0.00 sec)
begin;
root@yq [locktest]> update yqlock1 set a = 123 where b = '5';
Query OK, 2 rows affected (0.00 sec)
Rows matched: 2 Changed: 2 Warnings: 0
root@yq [locktest]> select ENGINE,ENGINE_TRANSACTION_ID,THREAD_ID,EVENT_ID,OBJECT_SCHEMA,OBJECT_NAME,INDEX_NAME,LOCK_TYPE,LOCK_MODE,LOCK_STATUS,LOCK_DATA from performance_schema.data_locks;
+--------+-----------------------+-----------+----------+---------------+-------------+------------+-----------+-----------+-------------+------------------------+
| ENGINE | ENGINE_TRANSACTION_ID | THREAD_ID | EVENT_ID | OBJECT_SCHEMA | OBJECT_NAME | INDEX_NAME | LOCK_TYPE | LOCK_MODE | LOCK_STATUS | LOCK_DATA |
+--------+-----------------------+-----------+----------+---------------+-------------+------------+-----------+-----------+-------------+------------------------+
| INNODB | 2287516 | 155889 | 18 | locktest | yqlock1 | NULL | TABLE | IX | GRANTED | NULL |
| INNODB | 2287516 | 155889 | 18 | locktest | yqlock1 | PRIMARY | RECORD | X | GRANTED | supremum pseudo-record |
| INNODB | 2287516 | 155889 | 18 | locktest | yqlock1 | PRIMARY | RECORD | X | GRANTED | 1 |
| INNODB | 2287516 | 155889 | 18 | locktest | yqlock1 | PRIMARY | RECORD | X | GRANTED | 3 |
| INNODB | 2287516 | 155889 | 18 | locktest | yqlock1 | PRIMARY | RECORD | X | GRANTED | 4 |
| INNODB | 2287516 | 155889 | 18 | locktest | yqlock1 | PRIMARY | RECORD | X | GRANTED | 5 |
| INNODB | 2287516 | 155889 | 18 | locktest | yqlock1 | PRIMARY | RECORD | X | GRANTED | 6 |
| INNODB | 2287516 | 155889 | 18 | locktest | yqlock1 | PRIMARY | RECORD | X | GRANTED | 2 |
+--------+-----------------------+-----------+----------+---------------+-------------+------------+-----------+-----------+-------------+------------------------+
- 條件無索引情況下摔踱,鎖全記錄虐先,并存在 supremum pseudo-record,也就是后續(xù)的也不能寫入(鎖全表)
RC隔離級(jí)別
條件字段有索引
【session1】
root@yq [locktest]> show variables like '%iso%';
+-----------------------+-----------------+
| Variable_name | Value |
+-----------------------+-----------------+
| transaction_isolation | REPEATABLE-READ |
+-----------------------+-----------------+
1 row in set (0.00 sec)
root@yq [locktest]> set session transaction_isolation = 'READ-COMMITTED';
Query OK, 0 rows affected (0.00 sec)
root@yq [locktest]> show variables like '%iso%';
+-----------------------+----------------+
| Variable_name | Value |
+-----------------------+----------------+
| transaction_isolation | READ-COMMITTED |
+-----------------------+----------------+
1 row in set (0.00 sec)
root@yq [locktest]> begin;
Query OK, 0 rows affected (0.00 sec)
root@yq [locktest]> update yqlock1 set b = '隔壁老王' where a = '5';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
root@yq [locktest]> select ENGINE,ENGINE_TRANSACTION_ID,THREAD_ID,EVENT_ID,OBJECT_SCHEMA,OBJECT_NAME,INDEX_NAME,LOCK_TYPE,LOCK_MODE,LOCK_STATUS,LOCK_DATA from performance_schema.data_locks;
+--------+-----------------------+-----------+----------+---------------+-------------+------------+-----------+---------------+-------------+-----------+
| ENGINE | ENGINE_TRANSACTION_ID | THREAD_ID | EVENT_ID | OBJECT_SCHEMA | OBJECT_NAME | INDEX_NAME | LOCK_TYPE | LOCK_MODE | LOCK_STATUS | LOCK_DATA |
+--------+-----------------------+-----------+----------+---------------+-------------+------------+-----------+---------------+-------------+-----------+
| INNODB | 2287524 | 156236 | 8 | locktest | yqlock1 | NULL | TABLE | IX | GRANTED | NULL |
| INNODB | 2287524 | 156236 | 8 | locktest | yqlock1 | idx_a | RECORD | X,REC_NOT_GAP | GRANTED | 5, 2 |
| INNODB | 2287524 | 156236 | 8 | locktest | yqlock1 | PRIMARY | RECORD | X,REC_NOT_GAP | GRANTED | 2 |
+--------+-----------------------+-----------+----------+---------------+-------------+------------+-----------+---------------+-------------+-----------+
3 rows in set (0.00 sec)
【session2】
root@yq [locktest]> begin;
Query OK, 0 rows affected (0.00 sec)
root@yq [locktest]> insert into yqlock1 select 7,'9','aaa';
Query OK, 1 row affected (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 0
root@yq [locktest]> insert into yqlock1 select 8,'10','aaa';
Query OK, 1 row affected (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 0
root@yq [locktest]> insert into yqlock1 select 9,'12','aaa';
Query OK, 1 row affected (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 0
root@yq [locktest]> insert into yqlock1 select 10,'15','aaa';
Query OK, 1 row affected (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 0
- 僅鎖住了相應(yīng)的條件行
更新的數(shù)據(jù)是不存在的記錄
【session1】
root@yq [locktest]> begin;
Query OK, 0 rows affected (0.00 sec)
root@yq [locktest]> update yqlock1 set b = '隔壁老王' where a = '12';
Query OK, 0 rows affected (0.00 sec)
Rows matched: 0 Changed: 0 Warnings: 0
root@yq [locktest]> select ENGINE,ENGINE_TRANSACTION_ID,THREAD_ID,EVENT_ID,OBJECT_SCHEMA,OBJECT_NAME,INDEX_NAME,LOCK_TYPE,LOCK_MODE,LOCK_STATUS,LOCK_DATA from performance_schema.data_locks;
+--------+-----------------------+-----------+----------+---------------+-------------+------------+-----------+-----------+-------------+-----------+
| ENGINE | ENGINE_TRANSACTION_ID | THREAD_ID | EVENT_ID | OBJECT_SCHEMA | OBJECT_NAME | INDEX_NAME | LOCK_TYPE | LOCK_MODE | LOCK_STATUS | LOCK_DATA |
+--------+-----------------------+-----------+----------+---------------+-------------+------------+-----------+-----------+-------------+-----------+
| INNODB | 2287531 | 156236 | 13 | locktest | yqlock1 | NULL | TABLE | IX | GRANTED | NULL |
+--------+-----------------------+-----------+----------+---------------+-------------+------------+-----------+-----------+-------------+-----------+
1 row in set (0.00 sec)
【session2】
root@yq [locktest]> begin;
Query OK, 0 rows affected (0.00 sec)
root@yq [locktest]> insert into yqlock1 select 10,'12','aaa';
Query OK, 1 row affected (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 0
- 僅有一個(gè)IX鎖派敷,不影響數(shù)據(jù)寫入蛹批,即使寫入的值是上述更新的值
條件字段無索引
root@yq [locktest]> begin;
Query OK, 0 rows affected (0.00 sec)
root@yq [locktest]> update yqlock1 set a = 123 where b = '5';
Query OK, 2 rows affected (0.02 sec)
Rows matched: 2 Changed: 2 Warnings: 0
root@yq [locktest]> select ENGINE,ENGINE_TRANSACTION_ID,THREAD_ID,EVENT_ID,OBJECT_SCHEMA,OBJECT_NAME,INDEX_NAME,LOCK_TYPE,LOCK_MODE,LOCK_STATUS,LOCK_DATA from performance_schema.data_locks;
+--------+-----------------------+-----------+----------+---------------+-------------+------------+-----------+---------------+-------------+-----------+
| ENGINE | ENGINE_TRANSACTION_ID | THREAD_ID | EVENT_ID | OBJECT_SCHEMA | OBJECT_NAME | INDEX_NAME | LOCK_TYPE | LOCK_MODE | LOCK_STATUS | LOCK_DATA |
+--------+-----------------------+-----------+----------+---------------+-------------+------------+-----------+---------------+-------------+-----------+
| INNODB | 2287537 | 156236 | 18 | locktest | yqlock1 | NULL | TABLE | IX | GRANTED | NULL |
| INNODB | 2287537 | 156236 | 18 | locktest | yqlock1 | PRIMARY | RECORD | X,REC_NOT_GAP | GRANTED | 1 |
| INNODB | 2287537 | 156236 | 18 | locktest | yqlock1 | PRIMARY | RECORD | X,REC_NOT_GAP | GRANTED | 5 |
+--------+-----------------------+-----------+----------+---------------+-------------+------------+-----------+---------------+-------------+-----------+
3 rows in set (0.00 sec)
- 可以看出僅對(duì)條件符合的行加鎖
- 其實(shí)在RC模式下撰洗,剛開始是鎖了全記錄的,MySQL評(píng)估WHERE條件后腐芍,將釋放不匹配行的記錄鎖差导。