Mysql 兩階段鎖和死鎖

巴啦啦發表於2022-10-12

兩階段鎖

概念

講的是InnoDB如何處理行鎖的上鎖,釋放鎖的行為。

事務使用過程中,對記錄以主鍵為條件刪改時,會立刻加上排他鎖,這完成了上鎖階段。

當刪改動作完成後,這個鎖並不會立即釋放,需要等至事務提交時,才會釋放鎖

引發的問題-阻塞

事務 A 事務 B
begin;
update t set k=k+1 where id=1;
update t set k=k+1 where id=2;
begin;
update t set k=k+2 where id=1;
commit;

根據兩階段鎖協議,事務 B將會因為id=1的資料被事務 A上了鎖,而阻塞,因為事務 B需要拿到鎖後才能進行下一步操作。

上述這個問題,可能看似問題不大,但是如果不止是事務 B,還有事務 C事務 D,等等很多,做的是跟事務 B一樣的事,問題就大了,被阻塞的執行緒就會多了起來。

如何處理上述問題

我們應該儘量將可能會引起阻塞的語句,放到事務的最後面操作,例如上述事務 A例子中的id=1的語句,它和第二句的執行並沒有什麼關聯關係,可是它是容易引起阻塞的語句,因為在事務 B中也要對這一行資料做鎖操作(在各類事務中頻繁使用的,如公司的收付款賬號餘額記錄,即**熱點行**),但是卻在事務一開始就拿到鎖了。

本質上,是縮短了拿鎖時間和釋放鎖之間的時間。即持有鎖的時間縮短,以此減少鎖引起的阻塞。

死鎖

概念

兩個執行緒,互相在等待對方釋放資源。

在兩個事務A,B中。

  1. 事務 A拿到了資源 A的鎖。
  2. 事務 B拿到了資源 B的鎖。
  3. 事務 A去拿資源 B的鎖。
  4. 事務 B去拿資源 A的鎖。

很明顯,步驟 3,4中,事務 A,B 都想去拿鎖,但是又都拿不到,因為對方都還沒有釋放該資源的鎖。這種現象就是死鎖。

引發的問題-死鎖

InnoDB中,有一個拿鎖的等待時間配置,超過這個時間就會丟擲異常,這個時間預設是50秒。通常來說,有一個介面需要50秒後才響應是不可接受的。innodb_lock_wait_timeout

那是不是把這個配置時間設定短一點就行了?比如1秒?

應該是不可以的,因為可能會影響到你的正常業務,或許你的業務導致你的事務執行時間本身就比較長,超過1秒。超出這個時間會丟擲異常,你的正常業務就被影響了。

那該如何處理上述問題

InnoDB中,還有一個自動檢測死鎖並處理的配置。它是預設開啟的,在極端情況下,雖然能處理問題,但是對CPU消耗特別大。

它原理是在事務中即將要上鎖的時候,會去檢測其他併發執行緒,有沒有將此資源鎖住,如果檢測到某個執行緒A有,然後再會去檢測執行緒A的依賴有沒有被其他併發執行緒鎖住,如此迴圈往復,最終判斷這些鎖會不會形成死鎖。

可以看出,執行緒越多,檢測成本就越大。innodb_deadlock_detect

僅代表個人當前的學習做出的對此問題的處理和總結:

1.關閉死鎖檢測,將拿鎖時間配置縮短至預估的最高時間,通常不會超過15秒,超過15秒後,需要有重試機制。

2.開啟死鎖檢測,在應用層控制併發連線數,使用連線池控制Mysql的連線數,在服務層限制Mysql最大連線數。

以上是對怎樣減少行鎖效能影響的學習總結。

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章