【每日鮮蘑】資料庫隔離級別、髒讀、幻讀、鎖等

襄垣發表於2020-03-26

線上環境中,偶爾會發生一些令人“我靠”的問題,其實一部分問題源於資料庫。當開發環境測試環境沒有出現的問題在線上環境中卻偶然出現時,這也是解決問題的一個方向。

髒讀

資料庫隔離級別為讀未提交的時候,可能發生髒讀讀未提交指當會話 A 的資料庫操作尚未commit時,會話 B 可以讀取到這個未提交的資料。而此時如果會話 A 因為某些原因rollback了,那麼會話 B 讀取的資料就是錯誤的,也就是髒讀。當隔離級別提高到讀已提交時,則可以避免髒讀

不可重複讀

簡單的理解就是多次讀取結果不一致,在會話 A 中多次相同的操作讀取的資料是不一致的。比如,在會話 A 的兩次讀取操作直接,會話 B 對此資料進行了提交,那麼會話 A 第一次讀取的是之前的資料,第二次讀取的是之後的資料。通過隔離級別可重複讀來解決這個問題。

幻讀

不可重複讀的特殊場景,不可重複讀主要是指在讀取某條記錄時發生,而幻讀指的是範圍。比如,會話 A 第一查詢年齡大於 18 的人,發現沒有資料,但當第二次進行查詢時,卻查詢到了資料。在序列化的隔離級別下,不會發生幻讀

資料庫鎖

排他鎖

又稱為 X 鎖,寫鎖。一個事務對資料物件 O 加了 排他鎖,就可以對 O 進行讀取和更新。加鎖期間其它事務不能對 O 加任何鎖。

共享鎖

又稱為 S 鎖,讀鎖。一個事務對資料物件 O 加了共享鎖,可以對 O 進行讀取操作,但是不能進行更新操作。加鎖期間其它事務能對 O 加 共享鎖,但是不能加排他鎖

隔離級別

讀未提交

如果一個事務已經開始寫操作,那麼其他事務則不允許同時進行寫操作,但允許其他事務讀此行資料。

  • 寫操作不加鎖;
  • 讀操作不加鎖;

讀已提交

讀取資料的事務允許其他事務繼續訪問該行資料,但是未提交的寫事務將會禁止其他事務訪問該行。

  • 寫操作加排他鎖,保持到事務結束;
  • 讀操作加鎖共享鎖,此次查詢結束後立即釋放共享鎖,保證讀取的資料都是已提交的資料。

可重複度

讀取資料的事務將會禁止寫事務(但允許讀事務),寫事務則禁止任何其他事務。保證了在同一個事務中多次讀取同樣資料的結果是一樣的。

  • 寫操作加排他鎖
  • 讀操作加共享鎖,保持到事務結束。不妨礙其他事務讀,但其它事務無法修改這些資料,無法鎖住insert的資料(造成幻讀);
  • InnoDB中,SELECTUPDATEDELETE操作的不可重複讀問題可以通過MVCC(多版本併發控制)來解決,但是INSERT操作的幻讀問題需要通過MVCC + Next-Key Locks 來解決。

序列化

極大的降低資料庫的併發能力。讀用讀鎖,寫用寫鎖,讀鎖和寫鎖互斥

  • 寫操作加排他鎖
  • 讀操作加排他鎖

總結

  1. InnoDB引擎中,對於索引的掃描,不僅鎖住掃描到的索引,而且還鎖住這些索引覆蓋的範圍,因此這個範圍是內插入資料是不允許的。
  2. 使用select @@tx_isolation;查詢資料庫的隔離級別。
  3. 隔離級別越高(讀未提交->讀已提交->可重複讀->序列化),越能保證資料的完整性和一致性,對併發效能的影響也會越大。
  4. Mysql預設的級別是可重複讀,優先考慮把資料庫系統的隔離級別設為讀已提交

本文使用 mdnice 排版

相關文章