資料庫死鎖排查思路分享(糾正篇)
來源:撿田螺的小男孩
前言
大家好,我是田螺。昨天發了一篇死鎖思路排查的文章,發現我舉的程式碼例子,跟實際的業務場景有一點出入。因為我個人做事情是比較嚴謹的,所以今天糾正一下,再發一次。
這篇文章,主要給大家講講資料庫死鎖的排查思路。
死鎖現場 排查思路 sql模擬 死鎖解決方案
死鎖場景現場
業務場景類似就是這樣:做使用者的資料遷移,醬紫:
把業務禮物表A的資料刪除,然後修改使用者ID後,然後插入到禮物B表。其中,A表和B表,表示同一個禮物邏輯表下的不同分表。
表結構:、
CREATE TABLE `gift_send_flow_0` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '主鍵',
`sender_id` int DEFAULT NULL COMMENT '贈送者ID',
`gift_type` varchar(50) NOT NULL COMMENT '禮物型別',
`gift_id` varchar(50) NOT NULL COMMENT '禮物ID',
`gift_name` varchar(100) NOT NULL COMMENT '禮物名稱',
`created_time` datetime DEFAULT NULL COMMENT '建立時間',
`updated_time` datetime DEFAULT NULL COMMENT '更新時間',
`gift_send_time` datetime DEFAULT NULL COMMENT '禮物贈送時間',
`quantity` int DEFAULT NULL COMMENT '禮物數量',
`receiver_id` int DEFAULT NULL COMMENT '接收者ID',
`message` text COMMENT '訊息',
`status` varchar(20) DEFAULT NULL COMMENT '狀態',
`expiry_time` datetime DEFAULT NULL COMMENT '過期時間',
`channel_no` varchar(50) DEFAULT NULL COMMENT '渠道',
`flow_no` varchar(50) NOT NULL COMMENT '流水號',
PRIMARY KEY (`id`),
UNIQUE KEY `idx_unique_flow_no` (`flow_no`)
) ENGINE=InnoDB AUTO_INCREMENT=37 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
在進行禮物流水錶資料遷移的過程中,出現了死鎖等待超時的場景。
從日誌可以看出,是在執行禮物贈送流水錶插入的時候,阻塞等待,最後鎖等待超時了。出現這種情況,一般都是因為產生了死鎖。
有些小夥伴覺得很奇怪:
既然是死鎖,為什麼出現的卻是
Lock wait timeout exceeded; try restarting transaction
鎖等待超時這個日誌呢?這是因為在Innodb儲存引擎中,當檢測到死鎖時,它會嘗試自動解決死鎖問題,通常是透過回滾(rollback)其中的一個或者多個事務來解除死鎖。
死鎖是如何產生的
既然是死鎖問題,我們先來回顧一下死鎖產生的條件。死鎖是多個程式或執行緒因競爭有限的資源而發生的一種相互等待的狀態,使得每個程式或執行緒都無法繼續執行。死鎖產生的條件包括:
互斥條件:至少有一個資源是獨佔的,即一次只能被一個程式或執行緒使用。 持有和等待條件:一個程式或執行緒可以持有一個資源,並等待其他程式或執行緒持有的資源。 非搶佔條件:已經分配給一個程式或執行緒的資源不能被強制性地搶佔,只能由持有資源的程式或執行緒顯式釋放。 迴圈等待條件:一系列程式或執行緒形成迴圈等待其他程式或執行緒持有的資源。
站在資料庫的角度,死鎖的表現如下:
死鎖排查思路
死鎖的排查思路是怎樣的呢?我一般是這麼排查的。
用 show engine innodb status
,檢視最近一次死鎖日誌。分析死鎖日誌,找到關鍵詞 TRANSACTION
分析死鎖日誌,檢視正在執行的SQL 看它持有什麼鎖,等待什麼鎖。
順著這個排查思路,我們先復現這個死鎖案例。在插入禮物贈送流水錶阻塞等待的過程,執行show engine innodb status
命令,檢視事務和鎖的資訊。
透過日誌,可以看到這個事務正在執行的SQL是:
INSERT INTO gift_send_flow_0 (id,gift_type, gift_id, gift_name, created_time, updated_time,
gift_send_time, quantity, sender_id, receiver_id, message, status
, expiry_time, channel_no,flow_no)
VALUES (null, '虛擬', '1', '玫瑰花', '2023-11-26 19:10:45', '2023-11-26 19:10:45', '2023-11-26 19:10:45', 1, 20000, 10025, '送給女嘉賓', null, null, '1000', 'flowNo666')
它在等待一個idx_unique_flow_no
的讀共享鎖。那麼到底是什麼SQL持有了這個鎖,導致它阻塞等待呢,這時候,我們聯絡上下文程式碼,把操作這個表相關的插入或者修改、刪除的SQL都梳理一下,最後發現是一條刪除的SQL涉及到:
<delete id="delByFLowNo">
DELETE FROM gift_send_flow WHERE flow_no = #{flowNo}
AND sender_id = #{sendId}
</delete>
我們遷移的過程,涉及把原來記錄刪除掉,然後替換senderId,再執行插入。基本確定就是刪除和插入的SQL形成的死鎖。我們再來本地模擬這兩條SQL的併發執行。
sql模擬死鎖復現
先開啟一個事務A,執行刪除:
mysql> BEGIN;
Query OK, 0 rows affected (0.01 sec)
mysql> DELETE FROM gift_send_flow_0 WHERE flow_no = 'flowNo666' AND sender_id = 10000;
Query OK, 1 row affected (0.00 sec)
另開一個事務,再執行插入,發現在執行的時候,就進入了阻塞等待。
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO gift_send_flow_0 (id,gift_type, gift_id, gift_name, created_time, updated_time, gift_send_time, quantity, sender_id, receiver_id, message, status , expiry_time, channel_no,flow_no) VALUES (null,'虛擬', '1', '玫瑰花', '2023-11-21 22:57:28', '2023-11-21 22:57:28', '2023-11-21 22:57:28', 1, 170000, 10025, '送給女嘉賓', NULL , NULL,'1000','flowNo666');
透過show engine innodb status
檢視死鎖日誌:
發現這個跟我們程式碼跑的一模一樣。為了進一步驗證,可以透過這個命令(MySQL 8.0+)檢視SQL加鎖情況:SELECT * FROM performance_schema.data_locks\G;
可以發現,當執行刪除SQL的時候,會給唯一索引 idx_unique_flow_no
加一個排他鎖。那就奇怪了,我們從剛才日誌可以發現,插入SQL等待的是一個idx_unique_flow_no
的讀共享鎖。為啥會衝突呢?其實是因為 讀共享鎖跟排他鎖是衝突的:
可以得出結果,delete
語句的時候,持有了唯一索引的排他行鎖,然後insert
的時候,也需要獲取這個索引的讀共享鎖,因此形成死鎖。
死鎖解決方案
因為併發執行刪除和插入同一個表,因此形成死鎖。
死鎖的方案解決方案有:
避免迴圈等待:保證資源分配的有序性,例如,定義一個全域性的資源申請順序,並要求所有程式按照這個順序申請資源。這樣可以避免迴圈等待的情況。 資源有序性:按照固定的順序獲取資源,避免多個程式在不同的順序下請求資源,導致迴圈等待的情況。 超時機制:當一個程式無法獲取所需資源時,設定一個超時機制,超過一定時間後放棄等待的資源並釋放自己所持有的資源,避免長時間等待。
回到本文的案例,那就是遷移資料的時候控制有序性,序列執行就好。
最後
本文這個例子呢,是我模擬的一個案例出來,主要就是給大家分享死鎖的排查思路。希望對大家有幫助哈。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70027826/viewspace-2997328/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- MySQL死鎖系列-線上死鎖問題排查思路MySql
- JAVA死鎖排查-效能測試問題排查思路Java
- 一次詭異的線上資料庫的死鎖問題排查過程資料庫
- 資料問題排查思路
- 面試官:什麼是死鎖?怎麼排查死鎖?怎麼避免死鎖?面試
- SpringBoot Seata 死鎖問題排查Spring Boot
- 資料庫篇:mysql鎖詳解資料庫MySql
- 記一次Oracle死鎖/阻塞排查Oracle
- 關於 SAP HANA 資料庫的死鎖問題(deadlock)資料庫
- 記一次神奇的Mysql死鎖排查MySql
- golang 執行時死鎖排查和檢測Golang
- (資料庫十)資料庫中的鎖機制以及死鎖產生的原因及解決辦法資料庫
- SQL Server 資料庫查詢死鎖的處理步驟SQLServer資料庫
- 線上併發事務死鎖問題排查
- 記一次公司倉庫資料庫伺服器死鎖過程資料庫伺服器
- #第9篇分享:python資料儲存-MySQL資料庫PythonMySql資料庫
- 死鎖問題排查過程-間隙鎖的復現以及解決
- 【疑難系列】 一個看起來是資料庫死鎖的問題資料庫
- 新加坡血庫資料出現網安漏洞 經專家舉報及時糾正
- MSSQL連線資料庫密碼獲取工具與原文數個錯誤糾正SQL資料庫密碼
- 資料庫篇:mysql事務原理之MVCC檢視+鎖資料庫MySqlMVC
- 破解分散式資料庫全域性死鎖難題 GBase 8c引領資料庫領域變革分散式資料庫
- 資料庫鎖機制資料庫
- 一個資料庫死鎖竟然被測試發現了,這你敢信資料庫
- 效能優化之資料庫篇2-事務與鎖優化資料庫
- 死鎖
- Java面試必問-死鎖終極篇Java面試
- mysql資料庫磁碟io高的排查MySql資料庫
- SQLServer 如何收集資料以排除 SQL 死鎖問題SQLServer
- MySQL的index merge(索引合併)導致資料庫死鎖分析與解決方案MySqlIndex索引資料庫
- 資料庫中的共享鎖與排他鎖資料庫
- 什麼是死鎖?如何解決死鎖?
- MySQL資料庫鎖介紹MySql資料庫
- MySQL資料庫-鎖詳解MySql資料庫
- 死鎖概述
- 程式設計面試題:編寫一個會造成資料庫死鎖的應用程式設計面試題資料庫
- 作業系統(5) 死鎖的概念 死鎖產生的必要條件 死鎖的處理策略 預防死鎖 避免死鎖 死鎖的檢測和解除 銀行家演算法作業系統演算法
- 技能篇:linux服務效能問題排查及jvm調優思路LinuxJVM