MySQL事務與樂觀鎖

  • 2019 年 11 月 26 日
  • 筆記

版權聲明:本文為部落客原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。

本文鏈接:https://blog.csdn.net/sxllllwd/article/details/102892055

最近感覺自己好像幹了一件蠢事,寫了一個事務包含A和B兩個操作,然後又在A中加了樂觀鎖,導致失敗率特別高。因此重新看了事務與樂觀鎖的資料。

一次封鎖 兩段鎖

一次封鎖法,就是方法的開始階段,已經預先知道會用到哪些數據,然後全部鎖住,在方法運行之後,再全部解鎖。可以有效避免循環死鎖。

兩段鎖協議

加鎖階段和解鎖階段。

加鎖階段:在任何數據進行讀操作之前都有申請獲得S鎖,在進行寫操作之前要申請並獲得X鎖,加鎖不成功,則事務進入等待狀態,直到加鎖成功才繼續。

解鎖階段:當事務釋放了一個封鎖之後,事務進入解鎖階段,在該階段只能進行解鎖操作而不能再加鎖。

兩段鎖協議可以保證事務的並發調度串列化(串列化很重要,尤其是在數據恢復和備份的時候),但是無法避免死鎖。

Update加行鎖

如果update更新的where語句中的篩選條件沒有索引,會導致MYSQL給整張表的所有數據加行鎖。在SQL運行過程中,mysql並不知道哪些數據行是符合where條件的(沒有索引)。如果一個條件無法通過索引快速過濾,存儲引擎層面就會將所有記錄加鎖後返回,再由MYSQL層進行過濾。

但是實際使用過程中,mysql做了一些改進,在MYSQL過濾條件,發現不滿足之後,會調用unlock_row方法,把不滿足條件的紀錄釋放鎖(違背了二段鎖協議的約束)。這樣做,保證了最後只會持有滿足條件紀錄上的鎖。但是每條記錄的加鎖操作還是不能省略的。

這種情況同樣適用於MYSQL的默認隔離級別可重複讀。對一個數據量很大的表做批量修改的時候,如果無法使用相應的索引,MYSQL 過濾數據的時候特別慢,就會出現雖然沒有修改某些行的數據,但是它們還是被鎖住了。

快照讀與當前讀

快照讀很可能讀取的是歷史數據,而不是資料庫當前數據。

在MVCC中:

  • 快照讀:就是select
    • select * from table ….;
  • 當前讀:特殊的讀操作,插入/更新/刪除操作,屬於當前讀,處理的都是當前的數據,需要加鎖。
    • select * from table where ? lock in share mode;
    • select * from table where ? for update;
    • insert;
    • update ;
    • delete;

Next-Key鎖

行鎖防止別的事務修改或刪除,GAP鎖防止別的事務新增,行鎖和GAP鎖結合形成的的Next-Key鎖共同解決了RR級別在寫數據時的幻讀問題。

參考文檔:

Innodb中的事務隔離級別和鎖的關係