Mysql從入門到精通- Mysql死鎖問題排查命令彙總

InnoDB是一個支持行鎖的存儲引擎,鎖的類型有:共享鎖(S)排他鎖(X)意向共享(IS)意向排他(IX)

InnoDB還將鎖細分爲如下幾種子類型:

  • record lock(RK)
    鎖直接加在索引記錄上面,鎖住的是key
  • gap lock(GK)
    間隙鎖,鎖定一個範圍,但不包括記錄本身。GAP鎖的目的,是爲了防止同一事務的兩次當前讀,出現幻讀的情況
  • next key lock(NK)
    RK+GK
  • insert intention lock(IK)
    如果插入前,該間隙已經由gap鎖,那麼Insert會申請插入意向鎖。因爲了避免幻讀,當其他事務持有該間隙的間隔鎖,插入意向鎖就會被阻塞。

鎖的持有兼容程度如下表

請求鎖\持有鎖 gap lock insert intention lock record lock next key lock
gap lock 兼容 兼容 兼容 兼容
insert intention lock 衝突 兼容 兼容 衝突
record lock 兼容 兼容 衝突 衝突
next key lock 兼容 兼容 衝突 衝突

那麼再回到死鎖日誌,可以知道 :

事務1正在獲取插入意向鎖
事務2正在獲取插入意向鎖,持有排他gap鎖

再看我們上面的鎖兼容表格,可以知道,gap lock和insert intention lock是不兼容的
那麼就可以推斷出:事務1持有gap lock,等待事務2的insert intention lock釋放;事務2持有gap lock,等待事務1的insert intention lock釋放,從而導致死鎖。
那麼新的問題就來了,事務1的intention lock 爲什麼會和事務2的gap lock 有交集,或者說,事務1要插入的數據的位置爲什麼會被事務2給鎖住?
讓我回顧一下gap lock的定義:
間隙鎖,鎖定一個範圍,但不包括記錄本身。GAP鎖的目的,是爲了防止同一事務的兩次當前讀,出現幻讀的情況
那爲什麼是gap lock,gap lock到底是基於什麼邏輯鎖的記錄?發現自己相關的知識儲備還不夠。那就開始調查。
調查後發現,噹噹前索引是一個普通索引的時候,會加一個gap lock來防止幻讀,此gap lock 會鎖住一個左開右閉的區間。假設索引爲xx_idx(xx_id),數據分佈爲1,4,6,8,12,當更新xx_id=9的時候,這個時候gap lock的鎖定記錄區間就是(8,12],也就是鎖住了xxid in (9,10,11,12)的數據,當有其他事務要插入xxid in (9,10,11,12)的數據時,就會處於等待獲取鎖的狀態。
ps:當前索引不是普通索引,而且是唯一索引等其他情況,請參考下面資料
MySQL 加鎖處理分析

回到我自己的案例中,重新屢一下事務1的執行過程:

UPDATE list_rate 
SET is_deleted = 1, update_time = now(), operator_id = 1 
WHERE hotel_id = 10007 AND room_type_id = 1 AND is_deleted = 0 AND rate_date IN ( '2018-11-10' , '2018-11-11' ) 

因爲普通索引
KEYhotel_date_idx(hotel_id,rate_date)
的關係 這段sql會獲取一個gap lock,範圍(2,11111]

INSERT INTO list_rate (hotel_id,rate_date,room_type_id,rate,create_time,update_time,operator_id,is_deleted) 
VALUES (10007,'2018-11-10',1,144, now(),now(),1,0) , (10007,'2018-11-11',1,148, now(),now(),1,0) 

這段sql會獲取一個insert intention lock (waiting)

再看事務2的執行過程

UPDATE list_rate 
SET is_deleted = 1, update_time = now(), operator_id = 1 
WHERE hotel_id = 10009 AND room_type_id = 1 AND is_deleted = 0 AND rate_date IN ( '2018-11-10' , '2018-11-11' ) 

因爲普通索引
KEYhotel_date_idx(hotel_id,rate_date)
的關係 這段sql也會獲取一個gap lock,範圍也是(2,11111](根據前面的知識,gap lock之間會互相兼容,可以一起持有鎖的)

INSERT INTO list_rate (hotel_id,rate_date,room_type_id,rate,create_time,update_time,operator_id,is_deleted) 
VALUES (10009,'2018-11-10',1,144, now(),now(),1,0) , (10009,'2018-11-11',1,148, now(),now(),1,0) 

這段sql也會獲取一個insert intention lock (waiting)

看到這裏,基本也就破案了。因爲普通索引的關係,事務1和事務2的gap lock的覆蓋範圍太廣,導致其他事務無法插入數據。
重新梳理一下:

transaction1 transaction2
update (10007,'2018-11-11') gap lock (2,11111]  
  update (10009,'2018-11-11') gap lock (2,11111]
insert wait lock  
  insert wait lock
  dead lock roll back
done  

所以從結果來看,一堆事務被回滾,只有10007數據被更新成功

結論

gap lock 導致了併發處理的死鎖

處理

在mysql默認的事務隔離級別(repeatable read)下,無法避免這種情況。只能把併發處理改成同步處理。或者從業務層面做處理。

知識庫levelup

共享鎖、排他鎖、意向共享、意向排他
record lock、gap lock、next key lock、insert intention lock
show engine innodb status

 

1、查詢是否鎖表

show OPEN TABLES where In_use > 0;

查詢到相對應的進程 === 然後 kill    id

2、查詢進程

    show processlist

 

補充:

查看正在鎖的事務

SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS; 

 

查看等待鎖的事務

SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS; 

 

1 show processlist;

 

SHOW PROCESSLIST顯示哪些線程正在運行。您也可以使用mysqladmin processlist語句得到此信息。如果您有SUPER權限,您可以看到所有線程。否則,您只能看到您自己的線程(也就是,與您正在使用的MySQL賬戶相關的線程)。如果有線程在update或者insert 某個表,此時進程的status爲updating 或者 sending data。

 

如果您得到“too many connections”錯誤信息,並且想要了解正在發生的情況,本語句是非常有用的。MySQL保留一個額外的連接,讓擁有SUPER權限的賬戶使用,以確保管理員能夠隨時連接和檢查系統(假設您沒有把此權限給予所有的用戶)。

 

Status

含義

Checking table

正在檢查數據表(這是自動的)。

Closing tables

正在將表中修改的數據刷新到磁盤中,同時正在關閉已經用完的表。這是一個很快的操作,如果不是這樣的話,就應該確認磁盤空間是否已經滿了或者磁盤是否正處於重負中。

Connect Out

複製從服務器正在連接主服務器。

Copying to tmp table on disk

由於臨時結果集大於tmp_table_size,正在將臨時表從內存存儲轉爲磁盤存儲以此節省內存。

Creating tmp table

正在創建臨時表以存放部分查詢結果。

deleting from main table

服務器正在執行多表刪除中的第一部分,剛刪除第一個表。

deleting from reference tables

服務器正在執行多表刪除中的第二部分,正在刪除其他表的記錄。

Flushing tables

正在執行FLUSH TABLES,等待其他線程關閉數據表。

Killed

發送了一個kill請求給某線程,那麼這個線程將會檢查kill標誌位,同時會放棄下一個kill請求。MySQL會在每次的主循環中檢查kill標誌位,不過有些情況下該線程可能會過一小段才能死掉。如果該線程程被其他線程鎖住了,那麼kill請求會在鎖釋放時馬上生效。

Locked

被其他查詢鎖住了。

Sending data

正在處理SELECT查詢的記錄,同時正在把結果發送給客戶端。

Sorting for group

正在爲GROUP BY做排序。

Sorting for order

正在爲ORDER BY做排序。

Opening tables

這個過程應該會很快,除非受到其他因素的干擾。例如,在執ALTER TABLE或LOCK TABLE語句行完以前,數據表無法被其他線程打開。正嘗試打開一個表。

Removing duplicates

正在執行一個SELECT DISTINCT方式的查詢,但是MySQL無法在前一個階段優化掉那些重複的記錄。因此,MySQL需要再次去掉重複的記錄,然後再把結果發送給客戶端。

Reopen table

獲得了對一個表的鎖,但是必須在表結構修改之後才能獲得這個鎖。已經釋放鎖,關閉數據表,正嘗試重新打開數據表。

Repair by sorting

修復指令正在排序以創建索引。

Repair with keycache

修復指令正在利用索引緩存一個一個地創建新索引。它會比Repair by sorting慢些。

Searching rows for update

正在講符合條件的記錄找出來以備更新。它必須在UPDATE要修改相關的記錄之前就完成了。

Sleeping

正在等待客戶端發送新請求。

System lock

正在等待取得一個外部的系統鎖。如果當前沒有運行多個mysqld服務器同時請求同一個表,那麼可以通過增加--skip-external-locking參數來禁止外部系統鎖。

Upgrading lock

INSERT DELAYED正在嘗試取得一個鎖表以插入新記錄。

Updating

正在搜索匹配的記錄,並且修改它們。

User Lock

正在等待GET_LOCK()。

Waiting for tables

該線程得到通知,數據表結構已經被修改了,需要重新打開數據表以取得新的結構。然後,爲了能的重新打開數據表,必須等到所有其他線程關閉這個表。以下幾種情況下會產生這個通知:FLUSH TABLES tbl_name, ALTER TABLE, RENAME TABLE, REPAIR TABLE, ANALYZE TABLE,或OPTIMIZE TABLE。

waiting for handler insert

INSERT DELAYED已經處理完了所有待處理的插入操作,正在等待新的請求。

 

大部分狀態對應很快的操作,只要有一個線程保持同一個狀態好幾秒鐘,那麼可能是有問題發生了,需要檢查一下。還有其他的狀態沒在上面中列出來,不過它們大部分只是在查看服務器是否有存在錯誤是才用得着。

 

2 show full processlist;

 

show processlist;只列出前100條,如果想全列出請使用show full processlist;

 

3 show open tables;

 

這條命令能夠查看當前有那些表是打開的。In_use列表示有多少線程正在使用某張表,Name_locked表示表名是否被鎖,這一般發生在Drop或Rename命令操作這張表時。所以這條命令不能幫助解答我們常見的問題:當前某張表是否有死鎖,誰擁有表上的這個鎖等。

 

show open tables from database;

 

 

 

4 show status like ‘%lock%’

 

查看服務器狀態。

 

 

 

5 show engine innodb status\G;

 

MySQL 5.1之前的命令是:show innodbstatus\G;,MySQL 5.5使用上面命令即可查看innodb引擎的運行時信息。

 

 

 

6 show variables like ‘%timeout%’;

 

查看服務器配置參數。

 

 

 

參考資料

 

mysql5.0經常出現 err=1205 - Lockwait timeout exceeded; try restarting transaction

http://www.imysql.cn/node/165

 

mysql show processlist命令詳解

http://www.cnblogs.com/JulyZhang/archive/2011/01/28/1947165.html

 

MySQL鎖

http://blog.csdn.net/c__ilikeyouma/article/details/8541195

 

SHOW INNODB STATUS提示語法錯誤?

http://www.itpub.net/thread-1454597-1-1.html

 

SHOW OPEN TABLES – what is in your tablecache

http://blog.sina.com.cn/s/blog_4d1f40c00100rsse.html

 

學習分享

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章