併發插入引起的死鎖問題排查
大家掛著VPN排查問題,也是有點意思.
不過當時並沒有排查出原因
上班之後,發現是客戶端的一個bug.本來應該傳送一個請求,但是卻傳送了大量的請求,應用層面又沒有做冪等設計,所以所有的請求都落到了資料庫層面。
資料庫是一個過程
環境
MySQL 5.6.14
事務隔離級別 讀提交
引起問題的邏輯大致如下:
-
if not exists (select UserID from award_free_firecracker_watch_common where UserID=pUserID and AwardDate=pToday and GiftID=pGift) then
- insert into award_free_firecracker_watch_common (UserID, AwardDate,GiftID) values (pUserID, pToday,pGift);
-
end if;
-
-
select AwardTotal into pOld from award_free_firecracker_common where UserID=pUserID and GiftID=pGift for update;
-
update award_free_firecracker_common set AwardTotal=AwardTotal+1,AwardLeft=AwardLeft+1 where UserID=pUserID and GiftID=pGift;
-
-
SELECT ROW_COUNT() into pUpdateCount;
-
if pUpdateCount>0 then
-
update award_free_firecracker_watch_common set WatchCount=WatchCount+1 where UserID=pUserID and AwardDate=pToday and GiftID=pGift ;
- end if;
因為這個過程也不是我寫的,大致的意思是先查詢award_free_firecracker_watch_common表,如果沒有這個使用者的記錄,則插入.
然後修改award_free_firecracker_common的記錄,最後在修改award_free_firecracker_watch_common的記錄.
MySQL的加鎖過程
https://dev.mysql.com/doc/refman/5.7/en/innodb-locks-set.html
INSERT sets an exclusive lock on the inserted row. This lock is an index-record lock, not a next-key lock (that is, there is no gap lock) and does not prevent other sessions from inserting into the gap before the inserted row.
Prior to inserting the row, a type of gap lock called an insert intention gap lock is set. This lock signals the intent to insert in such a way that multiple transactions inserting into the same index gap need not wait for each other if they are not inserting at the same position within the gap. Suppose that there are index records with values of 4 and 7. Separate transactions that attempt to insert values of 5 and 6 each lock the gap between 4 and 7 with insert intention locks prior to obtaining the exclusive lock on the inserted row, but do not block each other because the rows are nonconflicting.
If a duplicate-key error occurs, a shared lock on the duplicate index record is set. This use of a shared lock can result in deadlock should there be multiple sessions trying to insert the same row if another session already has an exclusive lock. This can occur if another session deletes the row. Suppose that an InnoDB table t1 has the following structure:
CREATE TABLE t1 (i INT, PRIMARY KEY (i)) ENGINE = InnoDB;
Now suppose that three sessions perform the following operations in order:
Session 1:
START TRANSACTION; INSERT INTO t1 VALUES(1);
Session 2:
START TRANSACTION; INSERT INTO t1 VALUES(1);
Session 3:
START TRANSACTION; INSERT INTO t1 VALUES(1);
Session 1:
ROLLBACK;
The first operation by session 1 acquires an exclusive lock for the row. The operations by sessions 2 and 3 both result in a duplicate-key error and they both request a shared lock for the row. When session 1 rolls back, it releases its exclusive lock on the row and the queued shared lock requests for sessions 2 and 3 are granted. At this point, sessions 2 and 3 deadlock: Neither can acquire an exclusive lock for the row because of the shared lock held by the other.
A similar situation occurs if the table already contains a row with key value 1 and three sessions perform the following operations in order:
Session 1:
START TRANSACTION; DELETE FROM t1 WHERE i = 1;
Session 2:
START TRANSACTION; INSERT INTO t1 VALUES(1);
Session 3:
START TRANSACTION; INSERT INTO t1 VALUES(1);
Session 1:
COMMIT;
The first operation by session 1 acquires an exclusive lock for the row. The operations by sessions 2 and 3 both result in a duplicate-key error and they both request a shared lock for the row. When session 1 commits, it releases its exclusive lock on the row and the queued shared lock requests for sessions 2 and 3 are granted. At this point, sessions 2 and 3 deadlock: Neither can acquire an exclusive lock for the row because of the shared lock held by the other.
可以看到,Insert的時候,對記錄上排它鎖和插入意向鎖.
併發的情況下,如果這個記錄已經上了排它鎖,則會嘗試給這個記錄上共享鎖.
如果有三個以上的併發執行緒,
第一個執行緒上了排它鎖,第二和第三個執行緒,看到該記錄有排他鎖,則嘗試給這個記錄上共享鎖。
一旦第一個執行緒回滾,則第二,第三執行緒擁有共享鎖的同時,都在申請排它鎖.這時候就出現了死鎖.
需要注意的是,假如第一個執行緒提交了,則第二個,第三個執行緒會報重複主鍵的錯誤,但是這時候,第二個,第三個執行緒,還是擁有這個記錄的共享鎖.第二,第三執行緒必須回滾.否則他們擁有的共享鎖不釋放.
回到最開始的問題.
三個執行緒同時insert award_free_firecracker_watch_common表,一個執行緒成功獲取排它鎖,其他兩個執行緒上共享鎖.
等獲取排他鎖的執行緒提交,兩個上共享鎖的執行緒,最後一步 update award_free_firecracker_watch_common表,則產生了死鎖。
他們都是在獲取了共享鎖的同時,申請排它鎖.
參考:
https://dev.mysql.com/doc/refman/5.7/en/innodb-locks-set.html
http://www.cnblogs.com/renolei/p/4673842.html
http://feiyang21687.github.io/Mysql-Transaction/
%20insert%E9%94%81%E6%9C%BA%E5%88%B6/
http://blog.csdn.net/and1kaney/article/details/51214001
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/29254281/viewspace-2133705/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 線上併發事務死鎖問題排查
- SpringBoot Seata 死鎖問題排查Spring Boot
- MySQL死鎖系列-線上死鎖問題排查思路MySql
- 併發技術5:死鎖問題
- Python | 淺談併發鎖與死鎖問題Python
- JAVA死鎖排查-效能測試問題排查思路Java
- 併發:死鎖
- 死鎖問題排查過程-間隙鎖的復現以及解決
- 線上問題排查:記一次 Redis Cluster Pipeline 導致的死鎖問題Redis
- [Java併發]避免死鎖Java
- 併發請求的重複插入問題
- MySQL死鎖問題MySql
- MySQL 死鎖問題分析MySql
- 解決庫存扣減及訂單建立時防止併發死鎖的問題
- insert引起的死鎖,起因主鍵衝突
- MySQL鎖等待與死鎖問題分析MySql
- 一次詭異的線上資料庫的死鎖問題排查過程資料庫
- 面試官:什麼是死鎖?怎麼排查死鎖?怎麼避免死鎖?面試
- InnoDB併發插入,居然使用意向鎖?
- 《淺入淺出MySQL》表鎖 行鎖 併發插入MySql
- 論 MySql InnoDB 如何通過插入意向鎖控制併發插入MySql
- 記一次神奇的Mysql死鎖排查MySql
- Oracle死鎖一例(ORA-00060),鎖表導致的業務死鎖問題Oracle
- 用分散式鎖解決併發問題分散式
- 4種Golang併發操作中常見的死鎖情形Golang
- 記一次Oracle死鎖/阻塞排查Oracle
- 解決Oracle死鎖問題步驟Oracle
- Java併發程式設計實戰(4)- 死鎖Java程式設計
- Seata 全域性鎖等待超時 問題排查
- golang 執行時死鎖排查和檢測Golang
- Mysql使用kill命令解決死鎖問題MySql
- 由Linux核心bug引起SSH登入緩慢問題的排查與解決Linux
- AdornerDecorator的CacheMode繫結和windows鎖屏導致TableControl鎖死問題Windows
- Tomcat 9.0.26 高併發場景下DeadLock問題排查與修復Tomcat
- 故障分析 | 從 Insert 併發死鎖分析 Insert 加鎖原始碼邏輯原始碼
- 故障解析丨一次死鎖問題的解決
- 關於 SAP HANA 資料庫的死鎖問題(deadlock)資料庫
- 資料庫死鎖排查思路分享(糾正篇)資料庫
- 記一次 MySQL select for update 死鎖問題MySql