在
線上環境
中,偶爾會發生一些令人“我靠”
的問題,其實一部分問題源於資料庫
。當開發環境
、測試環境
沒有出現的問題在線上環境
中卻偶然出現時,這也是解決問題的一個方向。
髒讀
資料庫隔離級別為
讀未提交
的時候,可能發生髒讀
。讀未提交
指當會話 A 的資料庫操作尚未commit
時,會話 B 可以讀取到這個未提交的資料。而此時如果會話 A 因為某些原因rollback
了,那麼會話 B 讀取的資料就是錯誤的,也就是髒讀
。當隔離級別提高到讀已提交
時,則可以避免髒讀
。
不可重複讀
簡單的理解就是
多次讀取結果不一致
,在會話 A 中多次相同的操作讀取的資料是不一致的。比如,在會話 A 的兩次讀取操作直接,會話 B 對此資料進行了提交,那麼會話 A 第一次讀取的是之前的資料,第二次讀取的是之後的資料。通過隔離級別可重複讀
來解決這個問題。
幻讀
不可重複讀
的特殊場景,不可重複讀
主要是指在讀取某條記錄時發生,而幻讀指的是範圍。比如,會話 A 第一查詢年齡大於 18 的人,發現沒有資料,但當第二次進行查詢時,卻查詢到了資料。在序列化
的隔離級別下,不會發生幻讀
。
資料庫鎖
排他鎖
又稱為 X 鎖,寫鎖。一個事務對資料物件 O 加了
排他鎖
,就可以對 O 進行讀取和更新。加鎖期間其它事務不能對 O 加任何鎖。
共享鎖
又稱為 S 鎖,讀鎖。一個事務對資料物件 O 加了
共享鎖
,可以對 O 進行讀取操作,但是不能進行更新操作。加鎖期間其它事務能對 O 加共享鎖
,但是不能加排他鎖
。
隔離級別
讀未提交
如果一個事務已經開始寫操作,那麼其他事務則不允許同時進行寫操作,但允許其他事務讀此行資料。
寫操作不加鎖; 讀操作不加鎖;
讀已提交
讀取資料的事務允許其他事務繼續訪問該行資料,但是未提交的寫事務將會禁止其他事務訪問該行。
寫操作加 排他鎖
,保持到事務結束;讀操作加鎖 共享鎖
,此次查詢結束後立即釋放共享鎖
,保證讀取的資料都是已提交的資料。
可重複度
讀取資料的事務將會禁止寫事務(但允許讀事務),寫事務則禁止任何其他事務。保證了在同一個事務中多次讀取同樣資料的結果是一樣的。
寫操作加 排他鎖
;讀操作加 共享鎖
,保持到事務結束。不妨礙其他事務讀,但其它事務無法修改這些資料,無法鎖住insert
的資料(造成幻讀
);InnoDB
中,SELECT
、UPDATE
、DELETE
操作的不可重複讀
問題可以通過MVCC(多版本併發控制)
來解決,但是INSERT
操作的幻讀
問題需要通過MVCC
+Next-Key Locks
來解決。
序列化
極大的降低資料庫的併發能力。
讀用讀鎖,寫用寫鎖,讀鎖和寫鎖互斥
寫操作加 排他鎖
;讀操作加 排他鎖
;
總結
在 InnoDB
引擎中,對於索引的掃描,不僅鎖住掃描到的索引,而且還鎖住這些索引覆蓋的範圍,因此這個範圍是內插入資料是不允許的。使用 select @@tx_isolation;
查詢資料庫的隔離級別。隔離級別越高( 讀未提交
->讀已提交
->可重複讀
->序列化
),越能保證資料的完整性和一致性,對併發效能的影響也會越大。Mysql
預設的級別是可重複讀
,優先考慮把資料庫系統的隔離級別設為讀已提交
。
本文使用 mdnice 排版