1. 程式人生 > >記一次Mysql線上死鎖

記一次Mysql線上死鎖

Mysql死鎖日誌解讀(SHOW ENGINE INNODB STATUS;)

2018-02-01 09:20:25 2b113e040700 INNODB MONITOR OUTPUT
=====================================
Per second averages calculated from the last 58 seconds
-----------------
BACKGROUND THREAD
-----------------
srv_master_thread loops: 11870079 srv_active, 0 srv_shutdown, 7504361 srv_idle
srv_master_thread log flush and writes: 19374440
----------
SEMAPHORES
----------
OS WAIT ARRAY INFO: reservation count 4759173
OS WAIT ARRAY INFO: signal count 58880148
Mutex spin waits 22460199905, rounds 24134283074, OS waits 2319277
RW-shared spins 22518808, rounds 379138472, OS waits 1277864
RW-excl spins 11822536, rounds 295048514, OS waits 1141953
Spin rounds per wait: 24.49 mutex, 16.84 RW-shared, 24.96 RW-excl
------------------------
LATEST DETECTED DEADLOCK
------------------------
2018-02-01 09:00:12 2b11cc7ce700
*** (1) TRANSACTION:
TRANSACTION 1413344039, ACTIVE 1.337 sec starting index read  -- 事務ID 1413344039
mysql tables in use 5, locked 5
LOCK WAIT 93 lock struct(s), heap size 13864, 46 row lock(s)
LOCK BLOCKING MySQL thread id: 11293490 block 11293500
MySQL thread id 11293500, OS thread handle 0x2b1127380700, query id 2765026522 event_scheduler Sending data 
--事務1 發生死鎖的語句
update tableB b inner join (select * from tableA b on a.InternalId=b.id)a set ........
*** (1) WAITING FOR THIS LOCK TO BE GRANTED: --等待的鎖資訊
-- space id+ page no + n bits 可以唯一確定一行記錄,等待該記錄的 S鎖(主鍵索引鎖)
RECORD LOCKS space id 866455 page no 56 n bits 168 index `PRIMARY` of table `testdb`.`tableA` trx id 1413344039 lock mode S locks rec but not gap waiting
Record lock, heap no 85 PHYSICAL RECORD: n_fields 33; compact format; info bits 0
 0: len 9; hex 424242445030303031; asc BBBDP0001;;
 1: len 6; hex 0000543c9520; asc   T<  ;;
 2: len 7; hex c10005401e359f; asc    @ 5 ;;
 3: len 4; hex 03fcd08b; asc     ;;
 4: len 24; hex e794b2e794b3e5aea4e58685e5bbbae7ad91e8aebee8aea1; asc                         ;;
 5: len 4; hex 80000258; asc    X;;
 6: SQL NULL;
 7: len 6; hex e8b4ade789a9; asc       ;;
 8: SQL NULL;
 9: SQL NULL;
 10: SQL NULL;
 11: len 0; hex ; asc ;;
 12: SQL NULL;
 13: SQL NULL;
 14: len 19; hex 323031352d30382d30352030303a30303a3030; asc 2015-08-05 00:00:00;;
 15: SQL NULL;
 16: SQL NULL;
 17: SQL NULL;
 18: len 1; hex 30; asc 0;;
 19: SQL NULL;
 20: len 19; hex 323031352d30382d30352030363a30393a3033; asc 2015-08-05 06:09:03;;
 21: len 19; hex 323031352d30382d30352030363a30393a3033; asc 2015-08-05 06:09:03;;
 22: len 4; hex 80000000; asc     ;;
 23: len 5; hex 474f555755; asc GOUWU;;
 24: len 4; hex 80000001; asc     ;;
 25: len 4; hex 80000000; asc     ;;
 26: len 4; hex 80000000; asc     ;;
 27: SQL NULL;
 28: SQL NULL;
 29: SQL NULL;
 30: SQL NULL;
 31: SQL NULL;
 32: len 4; hex 80000001; asc     ;;
 
*** (2) TRANSACTION:
TRANSACTION 1413343967, ACTIVE 4.817 sec fetching rows  --事務ID 1413343967
mysql tables in use 2, locked 2
116 lock struct(s), heap size 13864, 120 row lock(s)
MySQL thread id 11293490, OS thread handle 0x2b11cc7ce700, query id 2765024911 10.29.199.132 bd_db_admin Sending data
DELETE from tableA  where not EXISTS(select id from tableA_tmp  where tableA_tmp.id=tableA.id) and source=4
*** (2) HOLDS THE LOCK(S):  --已經持有S鎖
RECORD LOCKS space id 866455 page no 56 n bits 168 index `PRIMARY` of table `testdb`.`tableA` trx id 1413343967 lock_mode X locks rec but not gap  --獲取到該記錄行的 X 鎖
Record lock, heap no 85 PHYSICAL RECORD: n_fields 33; compact format; info bits 0
 0: len 9; hex 424242445030303031; asc BBBDP0001;;
 1: len 6; hex 0000543c9520; asc   T<  ;;
 2: len 7; hex c10005401e359f; asc    @ 5 ;;
 3: len 4; hex 03fcd08b; asc     ;;
 4: len 24; hex e794b2e794b3e5aea4e58685e5bbbae7ad91e8aebee8aea1; asc                         ;;
 5: len 4; hex 80000258; asc    X;;
 6: SQL NULL;
 7: len 6; hex e8b4ade789a9; asc       ;;
 8: SQL NULL;
 9: SQL NULL;
 10: SQL NULL;
 11: len 0; hex ; asc ;;
 12: SQL NULL;
 13: SQL NULL;
 14: len 19; hex 323031352d30382d30352030303a30303a3030; asc 2015-08-05 00:00:00;;
 15: SQL NULL;
 16: SQL NULL;
 17: SQL NULL;
 18: len 1; hex 30; asc 0;;
 19: SQL NULL;
 20: len 19; hex 323031352d30382d30352030363a30393a3033; asc 2015-08-05 06:09:03;;
 21: len 19; hex 323031352d30382d30352030363a30393a3033; asc 2015-08-05 06:09:03;;
 22: len 4; hex 80000000; asc     ;;
 23: len 5; hex 474f555755; asc GOUWU;;
 24: len 4; hex 80000001; asc     ;;
 25: len 4; hex 80000000; asc     ;;
 26: len 4; hex 80000000; asc     ;;
 27: SQL NULL;
 28: SQL NULL;
 29: SQL NULL;
 30: SQL NULL;
 31: SQL NULL;
 32: len 4; hex 80000001; asc     ;;
 
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
---等待 space id 866455 page no 257 n bits 168 該記錄的 X 鎖
RECORD LOCKS space id 866455 page no 257 n bits 168 index `PRIMARY` of table `testdb`.`tableA` trx id 1413343967 lock_mode X locks rec but not gap waiting
Record lock, heap no 58 PHYSICAL RECORD: n_fields 33; compact format; info bits 0
 0: len 4; hex 4a414743; asc JAGC;;
 1: len 6; hex 0000543c9520; asc   T<  ;;
 2: len 7; hex c1000540221eb8; asc    @"  ;;
 3: len 4; hex 03fcd483; asc     ;;
 4: len 12; hex e99d99e5ae89e5b9bfe59cba; asc             ;;
 5: len 4; hex 8000012c; asc    ,;;
 6: SQL NULL;
 7: len 6; hex e8b4ade789a9; asc       ;;
 8: SQL NULL;
 9: SQL NULL;
 10: SQL NULL;
 11: len 0; hex ; asc ;;
 12: SQL NULL;
 13: SQL NULL;
 14: len 19; hex 323031352d30382d30352030303a30303a3030; asc 2015-08-05 00:00:00;;
 15: SQL NULL;
 16: SQL NULL;
 17: len 4; hex 37383030; asc 7800;;
 18: len 6; hex 333130313030; asc 310100;;
 19: SQL NULL;
 20: len 19; hex 323031362d30382d30342030383a32313a3338; asc 2016-08-04 08:21:38;;
 21: len 19; hex 323031362d30382d30342030383a32313a3338; asc 2016-08-04 08:21:38;;
 22: len 4; hex 80000000; asc     ;;
 23: len 5; hex 474f555755; asc GOUWU;;
 24: len 4; hex 80000001; asc     ;;
 25: len 4; hex 8004baf0; asc     ;;
 26: len 4; hex 8004bb5a; asc    Z;;
 27: len 4; hex e220f342; asc    B;;
 28: len 4; hex e1dcf941; asc    A;;
 29: SQL NULL;
 30: SQL NULL;
 31: SQL NULL;
 32: len 4; hex 80000001; asc     ;;
 
*** WE ROLL BACK TRANSACTION (1)   --innodb會根據權重,選擇回滾的事務
------------
TRANSACTIONS

死鎖原因:

模擬示例

Innodb鎖粒度是行級別的,共享鎖(S)與排他鎖(X)是互斥的,當一方擁有了某行記錄的排他鎖後,另一方就不能其擁有共享鎖,同樣,一方擁有了其共享鎖後,另一方也無法得到其排他鎖。

測試樣例:


上圖中,事務2先申請了一個 id=3 記錄行的 S 鎖, 事務1接著申請一個排他鎖(修改操作會預設加上 X 鎖),此時由於 S X 兩鎖互斥,所以處於等待狀態,若事務2長時間不釋放,事務1會超時退出。

死鎖原因

從Mysql死鎖日誌中看出:事務2已經獲得了X鎖,還要去請求另一條記錄的X鎖。

所以推測出結論:

表一共有10行資料。

因為兩個事務獲取記錄行的順序不一樣,所以在某一時刻:

事務1,已經擁有 前5行資料的 S 鎖,需要獲取後5行記錄的S鎖

事務2,擁有後5行的X 鎖,需要獲取 前5行的X鎖

雙方都在等待需要的鎖,所以發生死鎖。


Select預設為什麼會發送S鎖?

一般的select不會發送 S 鎖,為什麼 線上事故中傳送了S鎖?

模仿線上事故sql:


猜想:在update中有子查詢 select的時候,子查詢select會預設傳送S鎖。

鎖等待的時候,查詢鎖狀態,發現等待的鎖是 S 鎖,即剛才的子查詢select傳送的 鎖。

解決思路:

1.將事務1 中的語句,先select出來,然後在update

參考