原文地址:MySQL 你好,死鎖mysql
在平常的生活中,相信你們曾或多或少有這麼一種體驗:"每到下班高峯期的時候,本來寬坦的交通幹道,一時間變得風雨不透,司機和乘客都煩躁不安,喇叭聲響成一片,當車卡在十字路口中間,會很尷尬的發現,此時不管想走哪都…..."。對於這樣的體驗,你們都是十分的懼怕接觸和體驗,交通部門也無時無刻爲解決交通擁堵問題而努力。git
其實上面生活案例中擁堵就相似於——高併發
場景;github
而全部方向的車堵在十字路口中間就相似於——數據庫死鎖
場景。算法
本章主要圍繞InnoDB存儲引擎
死鎖相關的一些概念、產生死鎖的緣由、死鎖場景以及死鎖的處理策略。sql
爲了更好的認識死鎖
,咱們先來了解MySQL
中與死鎖相關的一些基本概念。數據庫
併發控制(Concurrency control)指的是當多個用戶同時更新運行時,用於保護數據庫完整性的各類技術。安全
爲了保證數據庫的併發控制,所以MySQL
設置了兩種鎖:session
所謂鎖策略
就是在鎖的開銷和數據的安全性之間尋求平衡,這種平衡會影響到性能。目前InnoDB存儲引擎
有如下兩種鎖策略:併發
所謂事務,它是一個操做序列,這些操做要麼都執行,要麼都不執行,它是一個不可分割的工做單位。一個事務
是須要經過嚴格ACID測試的:高併發
SQL標準制定了四種隔離級別,規定事務的修改對其它事務是否可見
髒讀
髒讀
問題,但存在幻讀
問題(某個事務讀取記錄時,另外一事務插入了新紀錄,原事務再讀取記錄時產生幻行)。隔離級別 | 髒讀可能性 | 不可重複讀可能性 | 幻讀可能性 | 加鎖讀 |
---|---|---|---|---|
READ UNCOMMITED | Yes | Yes | Yes | No |
READ COMMITED | No | Yes | Yes | No |
REPEATABLE READ | No | No | Yes | No |
SERIALIZABLE | No | No | No | Yes |
死鎖是指兩個或多個事務
在同一資源上相互佔用,並請求鎖定
對方佔用的資源(我等待你的資源,你卻等待個人資源,咱們都相互等待,誰也不釋放本身佔有的資源),從而致使惡性循環的現象:
事務
試圖以不一樣順序鎖定資源時,就可能會產生死鎖事務
,同時鎖定
同一個資源時,也會產生死鎖死等和死鎖可不是一回事,若是你遇到了死等,大可放心,確定不是死鎖;若是發生了死鎖,也大可放心,絕對不會死等。
這是由於MySQL
內部有一套死鎖檢測機制,一旦發生死鎖會當即回滾一個事務,讓另外一個事務執行下去。而且這個死鎖回滾的的錯誤消息也會發送給客戶端。即便正常的業務中,死鎖也時不時會發生,因此遇到死鎖不要懼怕,由於這也是對數據安全的一種保護,可是若死鎖太頻繁,那可能會帶來許多的問題:
死鎖有四個必要的條件:
P0
等待的資源被P1
所佔有,P1
等待的資源被P2
所佔有,而P2
等待的又被P0
所佔有,造成了一個等待循環如下的全部場景是基於 InnoDB存儲引擎
而且隔離級別爲REPEATABLE-READ
(可重複讀)
查詢當前的隔離級別:
select @@global.tx_isolation,@@tx_isolation;
+-----------------------+-----------------+ | @@global.tx_isolation | @@tx_isolation | +-----------------------+-----------------+ | REPEATABLE-READ | REPEATABLE-READ | +-----------------------+-----------------+
修改隔離級別:
set global transaction isolation level read committed; ## 全局的 set session transaction isolation level read committed; ## 當前會話(session)
建立數據表
CREATE TABLE `deadlock` ( `id` int(11) NOT NULL, `stu_num` int(11) DEFAULT NULL, `score` int(11) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `idx_uniq_stu_num` (`stu_num`), KEY `idx_score` (`score`) ) ENGINE=InnoDB; insert into deadlock(id, stu_num, score) values (1, 11, 111); insert into deadlock(id, stu_num, score) values (2, 22, 222); insert into deadlock(id, stu_num, score) values (3, 33, 333);
id
主鍵索引
stu_num
爲惟一索引
score
普通索引
爲了模擬實際場景,須要在每一個會話(session)中執行如下兩條命令:
set autocommit=0; ## 關閉自動提交 START TRANSACTION; ## 開始事務
# session A select * from deadlock where id = 1 for update; # session B select * from deadlock where id = 2 for update; # session A select * from deadlock where id = 2 for update; ## 由於session2 已經給id=2分配了寫鎖 # session B select * from deadlock where id = 1 for update; ## 1213 - Deadlock found when trying to get lock; try restarting transaction
# session A SELECT * FROM deadlock WHERE id = 1 LOCK IN SHARE MODE; ## 獲取S-Lock # session B DELETE FROM deadlock WHERE id = 1; ## 想獲取X-Lock,但被session A的S-lock 卡住,目前處於waiting lock階段 # session A DELETE FROM deadlock WHERE id = 1; ## Error : Deadlock found when trying to get lock; try restarting transaction ## 想獲取X-Lock,sessionA自己擁有S-Lock ## 可是因爲sessionB 申請X-Lock再前## ## 所以sessionA不可以從S-lock 提高到 X-lock ## 須要等待sessionB 釋放才能夠獲取,因此形成死鎖
CREATE TABLE `deadlock_A` ( `id` int(11) NOT NULL, `stu_num` int(11) DEFAULT NULL, `score` int(11) DEFAULT NULL, PRIMARY KEY (`id`), KEY `idx_score` (`score`), KEY `idx_stu_num` (`stu_num`) USING BTREE ) ENGINE=InnoDB; # deadlock_A 數據 # select * from deadlock_A | id | stu_num | score | | ---- | ------- | ----- | | 1 | 11 | 111 | | 2 | 33 | 222 | | 3 | 22 | 333 | | 4 | 44 | 444 |
# session A delete from deadlock_A where stu_num > 11; ## 鎖二級索引(stu_num)的順序:22->33->44 鎖主鍵(id)索引的順序:3->2->4 # session B delete from deadlock_A where score > 111; ## 鎖二級索引(score)的順序:222->333->444 鎖主鍵(id)索引的順序:2->3->4 ## sessionA鎖主鍵3, sessionB鎖主鍵2 ## sessionA鎖主鍵2, sessionB鎖主鍵3 ## 死鎖產生-》AB BA ## 這個在併發場景,可能會產生。
CREATE TABLE `t2` ( `id` int(11) NOT NULL, `v` int(11) DEFAULT NULL, PRIMARY KEY (`id`), KEY `idx_v` (`v`) USING BTREE ) ENGINE=InnoDB; # select * from t2 | id | v | | ---- | ----- | | 2 | 2 | | 5 | 5 | | 10 | 10 |
# session A delete from test where v=5; # session B insert into t2 (id,v) values (3,3); ## ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction insert into t2 (id,v) values (9,9); ## ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction insert into t2 (id,v) values (5,11); ## ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction insert into t2 (id,v) values (1,1) ## Affected rows : 1, Time: 5.62sec insert into t2(id,v) values (10, 10); ## Affected rows : 1, Time: 10.51sec insert into t2 (id,v) values (9,11); ## Affected rows : 1, Time: 15.51sec
看得出鎖的是 id=5 & k=[3,10)的記錄。
經過上面案例,大概瞭解間隙鎖的範圍後,咱們來看看死鎖場景:
# session A update t2 set v = 5 where v =5; ## Affected rows : 1, Time: 12.67sec # session B update t2 set v = 10 where v =10; ## Affected rows : 1, Time: 12.88sec # session A insert into t2 (id,v) values (7,7); ## waiting # session B insert into t2 (id,v) values (8,8); ## Error : Deadlock found when trying to get lock; try restarting transaction
innodb_lock_wait_timeout 等待鎖超時回滾事務:
直觀方法是在兩個事務相互等待時,當一個等待時間超過設置的某一閥值時,對其中一個事務進行回滾,另外一個事務就能繼續執行。這種方法簡單有效,在innodb中,參數innodb_lock_wait_timeout用來設置超時時間。
wait-for graph算法來主動進行死鎖檢測:
innodb還提供了wait-for graph算法來主動進行死鎖檢測,每當加鎖請求沒法當即知足須要並進入等待時,wait-for graph算法都會被觸發。
《高性能的MySQL 第三版》
http://hedengcheng.com/?p=771...