mysql deadlock、Lock wait timeout解決和分析


 

項目上線 線上遇到大量的deadlock 和wait timeout 但是看程序沒什么問題 問dba也不能給出很好的解決方案!最終自己去了解mysql鎖 以及看mysq鎖日志 如果了解mysql鎖的機制下分析就很好解決

mysql的幾種鎖

X鎖(排他鎖) :

  與其他X鎖和S鎖互斥  

S鎖(共享鎖):

  與X鎖互斥 當一個事物獲得S鎖 別的事物可以繼續獲得S鎖 但是不能加X鎖 X鎖與X鎖和S鎖互斥

IX(意向排他鎖)

  IX是表級的 mysql引擎自動控制 在獲得X鎖之前 會先獲得IX鎖  IX只會與表級的S,X鎖互斥.  當mysql對表級進行加鎖(X或者S)的時候不用一行一行對數據判斷是否加了X鎖  直接根據是否有IX鎖來進行判斷,提高了效率

IS(意向共享鎖)

    IS是鎖是表級的  mysql引擎自動控制 在獲得S鎖之前會先獲得IS鎖  IS鎖只會與表級X鎖互斥  當mysql鎖對表級進行加X鎖是 不用一行一行對數據判斷是否加了S鎖  直接判斷是否存在表級的IS意向鎖

gap(鎖)

    間隙鎖 用於在指定索引位置區間加鎖 (只會在插入是互斥)

id age
1 10
2 20
3 30
4 40

  這個時候gap鎖就有[無窮小,10],[10,20],[20,30],[30,40][40,無窮大]  

  如果在RR模式下delete table where age=20 將不能插入10~20之間  20~30到之間的值

  gap鎖 只會在insert的時候互斥 (可以理解為gap在非Insert獲取的都是共享鎖  在Insert時獲取的是排他鎖)

 

next-key 

    行鎖和gap鎖的組合

當前讀與快照讀

快照讀

簡單的查詢  select * from student 

值得注意的是 在RC模式下 每次讀取都是新的快照  在RR模式下 當一次快照讀后 后面都會讀快照 

session1 session2
select * from student;  
  inser into student(name) values('小明')
  update student set name='小明' where id=1;
  delete student set name='小明2' where id=2;
  commit;
select * from student;  
commit;  

RC模式 session1第二次查詢 將會受到session2的操作的影響 因為RC模式每次讀取是讀取新的快照

RR模式 session1第二次查詢不會受到session2的影響 因為后面每次讀取都是讀取快照(session1 update insert delete也會更新快照)

ps:以上結果經過在mysql innodb模式 實踐

當前讀

select * student lock in share mode(共享鎖)

select * student for update(排他鎖)

insert

delete

update

快照讀是不加鎖的 當前讀 都會獲取相應的鎖

聚簇索引以及二級索引

mysql加鎖不是鎖住某一行數據而是在表的索引上面加鎖  理解聚簇索引和二級索引能夠很好的幫助我們理解鎖

什么是聚簇索引?

在innodb下  數據的存儲順序跟索引的存儲存儲順序是一樣  聚簇索引的頁節點就指向數據,每個表都會有一個聚簇索引 默認在主鍵上  如果沒有找到將會在表中的唯一非空的列上加上聚簇索引 如果沒有mysql將自動維護一個隱式的列作為聚簇索引

二級索引(非聚簇索引)

二級索引的 頁節點 存儲的是聚簇索引  當查詢一條非聚簇索引的列 會先根據索引找到聚簇索引 再根據聚簇索引查找數據

mysql RC和RR隔離級別的加鎖方式

現在有student表有以下數據

id name
1 a
2 b
3 d
4 f

 

RC模式

delete student where  id=1;

如果id列是主鍵

將會在id為1的的聚簇索引上加上X鎖

如果id列為唯一索引

將會在唯一索引上加上X鎖 同時根據唯一索引找到聚簇索引 並加鎖    為什么在唯一索引上面加了X鎖還要在聚簇索引上加鎖  因為如果這個時候另外一個事物根據聚簇索引更新數據 將感知不到鎖

比如上面表 name為聚簇索引  delete student where  id=1;   id=1的索引將加上鎖  這個但是聚簇索引name沒有加上 update  student id=2 where name=a 這個時候 name=a 獲取鎖成功能將能修改成功

上面的解釋但是會有疑惑 既然加鎖都加載聚簇索引上為什么還要多此一舉的在非聚簇索引列上加鎖(個人理解 判斷鎖互斥時 直接根據條件的索引 而不用再次根據索引找到聚簇索引)

如果id列非唯一索引

跟唯一索引加鎖機制一樣

如果id列無索引

將在走聚簇索引 全表掃描不管是否滿足條件的數據都會加上X鎖  但是RC模式有優化 不滿足條件的加鎖之后又會釋放

RR模式

如果id列是主鍵

將會在滿足條件的聚簇索引上加上X鎖

如果id列為唯一索引

 

在id列上加上X鎖同時在 對應的聚簇索引加上X鎖

如果id列非唯一索引

將在id列上加上next-key鎖 同時在聚簇索引加上x鎖

如果id列無索引

將在走聚簇索引 全表掃描不管是否滿足條件的數據都會加上next-key鎖  跟RC模式不同的是將不會釋放

  

deadlock分析

環境:RR隔離級別

線上報大量的deadlock 通過命令登錄mysql 通過此SHOW ENGINE INNODB STATUS\G將打印最近一次死鎖

定位到代碼 有這樣一個操作

1.delete from dealer_order_item where dealer_order_code='1111'

2.insert dealer_order_item(dealer_order_code,.....) values('1111',....)

dealer_order_code 沒有索引

通過上面的鎖分析 RR模式下如果當前讀沒有滿足條件的數據 整個表每一條數據都將會加上next-key鎖如下

session1 session2
delete from dealer_order_item where dealer_order_code='1111'  
  delete from dealer_order_item where dealer_order_code='2222'
insert dealer_order_item(dealer_order_code,.....) values('1111',....)  
   

結果:session1執行insert會死鎖 session2執行delete會等待

1.session1 delete全表掃描獲得所有行的gap鎖和x鎖

2.session2執行 delete全表掃描獲得gap鎖 然后鎖等待session釋放x鎖

3.session2執行 insert 嘗試獲得gap鎖 因為session2已經拿到gap鎖但是未拿到x鎖, 所以不能插入等待 因為session2也在等待session1釋放x鎖(所以死鎖)

情況2(未驗證過  生產環境遇到過2個insert死鎖情況)

如果where條件都不存在 2個delete都拿到gap鎖(鎖無窮大)而沒有行鎖 然后各自執行insert相互等待gap鎖 導致死鎖

 

waitlock分析

表數據id為主鍵索引 name為非唯一索引 

 

 

select * from information_schema.innodb_trx;表 

trx_id(事物id) trx_state(事物狀態) trx_started(事物開始時間) trx_requested_lock_id trx_wait_started(事物開始等待時間) trx_weight trx_mysql_thread_id(事物線程id) trx_query(具體sql) trx_operation_state(事物當前操作狀態) trx_tables_in_use(事物中多少個表被使用) trx_tables_locked(事物鎖了多少個表) trx_lock_structs trx_lock_memory_bytes(事物鎖住的內存大小) trx_rows_locked(事物擁有多少個鎖) trx_rows_modified(事物修改的行數) trx_concurrency_tickets(事物並發票數) trx_isolation_level(事物隔離級別) trx_unique_checks(是否唯一檢查) trx_foreign_key_checks(是否外鍵檢查) trx_last_foreign_key_error(最后的外鍵錯誤) trx_adaptive_hash_latched trx_adaptive_hash_timeout trx_is_read_only trx_autocommit_non_locking
7842656 LOCKWAIT  2019-01-14 10:22:04 7842656:25:4:4   2019-01-14 10:22:04 8 3733599  update demo set name='5555' where name='3333'   updating or deleting  1136  10  0  REPEATABLE READ 1  NULL  0  0
 7842647  RUNNING 2019-01-14 10:21:37  NULL   NULL  7 3733596  NULL  NULL   0  1 1136   7  REPEATABLE READ  1 NULL   0  0  0

select * from information_schema.innodb_lock_waits;表

requesting_trx_id(請求鎖的事物id) requested_lock_id(請求鎖的鎖id) blocking_trx_id(當前擁有鎖的事物id) blocking_lock_id(當前擁有鎖鎖id)
7842656 7842656:25:4:4 7842647 7842647:25:4:4

select * from information_schema.innodb_locks;表

lock_id(鎖id) lock_trx_id lock_mode(鎖模式) lock_type(鎖類型) lock_table(被做鎖的表) lock_index(被鎖的索引) lock_space(被鎖的表空間號) lock_page(被鎖的頁號) lock_rec(被鎖的記錄號) lock_data(被鎖的數據)
7842656:25:4:4 7842656(擁有鎖的事物id) X,GAP RECORD `dms`.`demo` index_name 25 4 4 '6666', 2
7842647:25:4:4 7842647 X RECORD `dms`.`demo` index_name 25 4 4 '6666', 2

 

1. 先看 表1  事物id 7842656等待7842647釋放鎖 trx_rows_locked字段看命名像是鎖了多少數據 不過我根據數據分析應該是獲得了多少個鎖 name索引加上聚簇索引id的鎖+3個gap鎖等於10 

ctrl+f 搜索:7842647 看關系更佳

 

2.分析表2 事物id7842656等待事物id7842647釋放7842647:25:4:4這個鎖

3.分析表3就清晰很多了 事物id7842656是請求這個鎖7842656:25:4:4

4.結論 session 2 嘗試修改name為5555會獲得索引為5555的x鎖和gap鎖 但是被seesion1獲得沒釋放 所以造成鎖等待

mysql 鎖等待分析相關表

information_schema.innodb_trx表

包含了正在InnoDB引擎中執行的所有事務的信息,包括waiting for a lock和running的事務

information_schema.innodb_lock_waits表

包含了blocked的事務的鎖等待的狀態

information_schema.innodb_locks表

主要包含了InnoDB事務鎖的具體情況,包括事務正在申請加的鎖和事務加上的鎖。

如何避免deadlock和wait lock

delete update 避免使用非索引字段為條件

 RR隔離級別將會走聚簇索引 全表掃描為每一行加上next-key鎖  注:RC隔離級別會逐行加X鎖 並釋放

可以這樣理解:delete update  掃描一條就會為一條加上鎖   當沒有索引會全表掃描 RR隔離級別掃描一條就會為這條加上鎖 並不會釋放   RC隔離級別掃描一條就會為這條加上鎖 如果不滿足條件的加上鎖之后 會自動釋放

name非索引條件  用於修改條件 雖然有滿足條件數據 也會導致全表掃描並逐行加X鎖  

我們為name加上索引條件再進行測試

alter table demo add index index_name(name);

 可以發現修改成功並不會等待

避免批量修改刪除避免無序

1.session修改id為1的數據 session2修改id為4的數據各自拿到next-key鎖

2.session1 修改id為4的數據 等待session2釋放next-key鎖  session2修改id為1的數據等待session1釋放next-key鎖 造成死鎖

批量修改或者刪除 統一排序一下 比如這里根據id 就不會出現交叉修改依賴

避免隱式轉換

session1 name為varchar  確傳入number導致隱式轉換走聚簇索引全表掃描 導致整個表都鎖了

記錄一個小插曲

可以看到 name有索引也沒有隱式轉換 也鎖了整個表。因為這個是mysql的優化機制當掃描的數據超過全表的20%~30%時 即便有二級索引也會走掃描整個聚簇索引( 個人測試針對當前讀是這樣 select不受影響)

避免where條件全表掃描

其實總結上面 可以發現 當前讀是根據where條件 掃描一條就加一個鎖

避免操作不存在的數據

修改或者編輯 最好先判斷數據不存在 

我們觀察一下鎖的情況

可以發現操作不存在數據會觸發gap對應索引排序的gap鎖 鎖無窮大 影響插入數據(應該在只會在RR模式上出現  不過在不確定數據是否存在 操作之前先判斷是否存在 是個好習慣)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM