mysql-鎖-鎖類型
record lock
A record lock is a lock on an index record(record lock 是一個加在索引記錄上的鎖),如果一個表定義時沒有索引,innodb會隱式的創建聚簇索引,名稱爲
GEN_CLUST_INDEX
,然後給這個隱式的鎖引加record lock
顯示的有索引
現有表b, 其中 id
爲主鍵:
CREATE TABLE `b` (
`id` int NOT NULL,
`num` int DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
插入數據:
INSERT INTO `b` (`id`, `num`) VALUES ('4', '3'), VALUES ('10', '5'), ('15', '6');
事務查詢與修改:
事務A | 事務B
mysql> begin; | mysql> begin;
Query OK, 0 rows affected | Query OK, 0 rows affected
|
mysql> select * from b where id=4 for update; |
+----+-----+ |
| id | num | |
+----+-----+ |
| 4 | 3 | |
+----+-----+ |
1 row in set |
| mysql> update b set num=8 where id=4;
| waiting....
查看innodb狀態:
show engine innodb status
update b set num=8 where id=4
------- TRX HAS BEEN WAITING 3 SEC FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 3 page no 4 n bits 80 index PRIMARY of
table `spring`.`b` trx id 3149 lock_mode X locks rec but not gap waiting
Record lock, heap no 5 PHYSICAL RECORD: n_fields 4; compact format; info bits 128
0: len 4; hex 80000004; asc ;;
1: len 6; hex 000000000c44; asc D;;
2: len 7; hex 010000011c0151; asc Q;;
3: len 4; hex 80000003; asc ;;
注意: lock_mode X locks rec but not gap, 沒有使用gap lock
沒有加任何索引
如果去掉 id 列的主鍵索引:
CREATE TABLE `b` (
`id` int DEFAULT NULL,
`num` int DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
執行相同的事務查詢和修改,此時查看innodb狀態爲:
RECORD LOCKS space id 4 page no 4 n bits 72
index GEN_CLUST_INDEX of table `spring`.`b` trx id 3182 lock_mode X waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 5; compact format; info bits 0
0: len 6; hex 00000000020b; asc ;;
1: len 6; hex 000000000c4e; asc N;;
2: len 7; hex 80000000000000; asc ;;
------------------
Gap lock
A gap lock is a lock on a gap between index records, or a lock on the gap before the first or after the last index record.(gap lock是鎖在索引記錄之間的間隙的鎖,或者是鎖在第一索引記錄之前或者最後一條索引記錄之後間隙間的鎖o)
現有表:
CREATE TABLE `t` (
`id` int DEFAULT NULL,
`num` int DEFAULT NULL,
KEY `index_id` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
數據:
INSERT INTO `spring`.`t` (`id`, `num`) VALUES
('10', '1'), ('11', '3'), ('13', '5'),('20', '5');
事務執行,在事務A中鎖住id在13和20之間的數據,事務B插入id爲15的數據:
mysql> begin; | mysql> begin;
Query OK, 0 rows affected | Query OK, 0 rows affected
|
mysql> select * from t where
id between 13 and 20 for update; |
+----+-----+ |
| id | num | |
+----+-----+ |
| 13 | 5 | |
| 20 | 5 | |
+----+-----+ |
2 rows in set |
|
mysql> |
|
| mysql> INSERT INTO `t` (`id`, `num`)
| VALUES ('15', '5');
| 1205 - Lock wait timeout exceeded;
| try restarting transaction
查看innodb狀態:
RECORD LOCKS space id 2 page no 5 n bits 72
index index_id of table `spring`.`t` trx id 3184 lock_mode X locks gap
before rec insert intention waiting
Record lock, heap no 5 PHYSICAL RECORD: n_fields 2;
compact format; info bits 0
0: len 4; hex 80000014; asc ;;
1: len 6; hex 000000000203; asc ;;
Next-Key Locks
A next-key lock is a combination of a record lock on the index record and a gap lock on the gap before the index record.(next-key lock,是索引記錄上的record lock和加在在這個索引記錄之前間隙之間的gap lock的組合,即左開右閉,(…,index record])
- next-key解決了事務中的幻讀現象(事務A讀取到事務B新增的數據)
- 可以通過設置事務隔離級別爲
read committed
來取消 next-key lock
現有表,其中 id 爲普通索引:
mysql> select * from t;
+----+-----+
| id | num |
+----+-----+
| 10 | 1 |
| 11 | 3 |
| 13 | 5 |
| 20 | 5 |
+----+-----+
4 rows in set
那麼這個索引所包含的可能 next-key lock爲:
(negative infinity, 10]
(10, 11]
(11, 13]
(13, 20]
(20, positive infinity)
簡單的來講,事務A查詢如下:
select * from t where id > 11 for update
事務B進行一次插入操作:
insert into t(id,num)values(18,4);
然後事務A進行一次查詢:
select * from t
好了,問題來了,如果沒有這個 next-key lock,而傳統的 RR 隔離級別是不能解決幻讀的問題,所以事務A就會讀取到時事務B提交的新增數據
即事務A鎖到的行數據就只有id=13,20,即在這些範圍之內是不能被插入的,但是其他空隙是可以插入,就比如這個id=18,這條數據是允許插入的,即出現幻讀現象,
但是,正因爲這個 next-key lock,使得事務A就會讀取不到時事務B提交的新增數據,解決了幻讀
因爲這個 next-key lock, select * from t where id > 11 for update
,這個sql鎖住的範圍是 (11, positive infinity),即鎖住了大於11之後的所有值,在這個範圍之內新增數據都是不可以的,比如這個18,也是被禁的,即解決了幻讀現象