【MySQL】gap lock 淺析
【定義】
innodb 行級鎖 record-level lock大致有三種:record lock, gap lock and Next-KeyLocks。
record lock 鎖住某一行記錄
gap lock 鎖住某一段範圍中的記錄
next key lock 是前兩者效果的疊加。
下面是MYSQL官方文件中相關內容的連結
http://dev.mysql.com/doc/refman/5.1/en/innodb-record-level-locks.html
【實驗環境】
session 1 20:39:29> show create table gap \G
*************************** 1. row ***************************
Table: gap
Create Table: CREATE TABLE `gap` (
`id` int(11) DEFAULT NULL,
KEY `ind_gap_id` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
session 1 20:39:32> select * from gap;
+------+
| id |
+------+
| 17 |
| 20 |
| 33 |
| 39 |
| 42 |
| 43 |
+------+
6 rows in set (0.00 sec)
【實驗】
兩個會話都在REPEATABLE-READ 事務隔離級別。且都要在事務中進行。
session 1 20:39:37> start transaction;
Query OK, 0 rows affected (0.00 sec)
session 1 20:39:41> delete from gap where id=33;
Query OK, 1 row affected (0.00 sec)
session 20:40:07>
在會話2中 插入id <20 和 >=39的值 可以執行成功,而當要插入的id [20,39)的值時 會遇到gap lock 。
session 2 20:40:15> start transaction;
Query OK, 0 rows affected (0.00 sec)
session 2 20:40:30> insert into gap values(14);
Query OK, 1 row affected (0.00 sec)
session 2 20:40:59> insert into gap values(18);
Query OK, 1 row affected (0.00 sec)
session 2 20:41:06> insert into gap values(20);
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
session 2 20:41:12> insert into gap values(24);
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
session 2 20:42:17>
session 2 20:42:53> insert into gap values(35);
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
session 2 20:44:09>
session 2 20:44:56> insert into gap values(39);
Query OK, 1 row affected (0.00 sec)
session 2 20:45:13> insert into gap values(40);
Query OK, 1 row affected (0.00 sec)
從上面的實驗中可以看出會話1 執行刪除語句之後,不僅僅鎖住 id=33的記錄,同時也鎖住區間為[20,39)的記錄。具體的原因是執行delete from gap where id=33語句,mysql 會執行索引掃描並在該表上施加一個next-key lock ,向左掃描到20,向右掃描到39 ,鎖定區間左閉右開,所以lock的範圍是 [20,39)。
【gap 鎖帶來的問題】
生產環境中有這樣的一個情況:
程式會對一個表message 進行update 和insert
session 1
UPDATE message SET gmt_modified = now(),deal_times = deal_times +1 , status = 'sending' , gmt_retry = '2012-11-17 23:54:10'
WHERE message_id=18;
insert into message (body ,user_id,status,message_type,version,deal_times,gmt_create,gmt_modified,gmt_retry)
values ('hello !',-1,'sending','instance_status_sync',2,127,now(),now(),now());
session 2
UPDATE message SET gmt_modified = now(),deal_times = deal_times +1 , status = 'sending' , gmt_retry = '2012-11-17 23:54:10'
WHERE message_id=19;
insert into message (body ,user_id,status,message_type,version,deal_times,gmt_create,gmt_modified,gmt_retry)
values ('hello world!',-2,'sending','instance_status_sync',1,17,now(),now(),now());
對於上述程式在無併發情況下,執行正常,但是併發量大的情況下,執行順序可能就會變成下面的:
UPDATE message SET gmt_modified = now(),deal_times = deal_times +1 , status = 'sending' , gmt_retry = '2012-11-17 23:54:10'
WHERE message_id= 61;
UPDATE message SET gmt_modified = now(),deal_times = deal_times +1 , status = 'sending' , gmt_retry = '2012-11-17 23:54:10'
WHERE message_id= 73;
insert into message (body ,user_id,status,message_type,version,deal_times,gmt_create,gmt_modified,gmt_retry)
values ('hello world!',-2,'sending','instance_status_sync',1,17,now(),now(),now());
insert into message (body ,user_id,status,message_type,version,deal_times,gmt_create,gmt_modified,gmt_retry)
values ('hello !',-1,'sending','instance_status_sync',2,127,now(),now(),now());
此時 往往會報錯
[ERROR] Could not execute Write_rows event on table db.message; Deadlock found when trying toget lock; ; try restarting transaction, Error_code: 1213;
前兩條update 型別的語句都已經獲得了[59,75 )區間內記錄的S鎖,然後兩個事務又分別對該區間段內的message_id=10這個位置請求X鎖,這時就發生死鎖,誰都請求不到X鎖,因為互相都持有S鎖。
【解決方案有兩種】
1、改變程式中資料庫操作的邏輯
2、取消gap lock機制
Gap locking can be disabled explicitly.This occurs if you change the transaction isolation level to READ COMMITTED orenable the innodb_locks_unsafe_for_binlog system variable.
【參考】
http://dev.mysql.com/doc/refman/5.1/en/innodb-record-level-locks.html
http://dev.mysql.com/doc/refman/5.1/en/innodb-locks-set.html
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/22664653/viewspace-750824/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- MYSQL INNODB replace into 死鎖 及 next key lock 淺析MySql
- MySQL Replication淺析MySql
- MySql(一) 淺析MySql索引MySql索引
- MySQL 高可用淺析MySql
- MySQL高可用淺析MySql
- 如何開啟和關閉MySQL 間隙鎖(gap lock)MySql
- MySQL RC隔離級別下罕見的gap lockMySql
- 淺析MySQL replace into 的用法MySql
- 【MySQL】八、double write 淺析.MySql
- MySQL事務原理淺析MySql
- mysql之 double write 淺析MySql
- 【MySQL】replace into 淺析之一MySql
- 【MySQL】replace into 淺析之二MySql
- java多執行緒:synchronized和lock比較淺析Java執行緒synchronized
- MySql(四) InnoDB事務淺析MySql
- MySQL5.7GTID淺析MySql
- 淺析MySQL 8.0直方圖原理MySql直方圖
- 淺析MySQL InnoDB的隔離級別MySql
- 技術分享 | MySQL : SSL 連線淺析MySql
- MySQL binlog相關原始碼淺析MySql原始碼
- 淺析Mysql的my.ini檔案MySql
- 關於 ReentrantLock 中鎖 lock() 和解鎖 unlock() 的底層原理淺析ReentrantLock
- MySQL伺服器連線過程淺析MySql伺服器
- 淺析MySQL事務中的redo與undoMySql
- MySQL的共享鎖阻塞會話案例淺析MySql會話
- MySQL效能最佳化淺析及線上案例MySql
- MySQL 非同步驅動淺析 (一):效能分析MySql非同步
- iOS Block淺淺析iOSBloC
- Mysql加鎖過程詳解(8)-理解innodb的鎖(record,gap,Next-Key lock)MySql
- 淺析 JWTJWT
- MongoDB淺析MongoDB
- RunLoop 淺析OOP
- Nginx淺析Nginx
- 淺析 requestAnimationFramerequestAnimationFrame
- 淺析PromisePromise
- 淺析GitGit
- 淺析RedisRedis
- Jvm 淺析JVM