mysql髒讀、不可重複讀、幻讀

  1. 髒讀(側重點在讀取了未提交的數據)

指一個事務讀取到了另一個事務未提交的數據,造成了與數據庫中的數據不一致的情況。

比如事務A修改了一條數據,但沒有提交,此時事務B卻讀取了該條數據,事務A由於出錯發生了回滾,這時事務B就形成了髒讀。

 

    2. 不可重複讀(虛讀)(側重點在讀取了已經提交的修改的數據,數據本身對比)

指一個線程中的事務讀取到了另外一個線程中的事務已提交的update的數據。即一個事務A兩次或多次讀取數據,在此期間,事務B讀取同一數據,並修改了此數據,然後提交了,造成了事務A兩次或多次讀取數據出現了數據不一致的情況。

不可重複讀可能造成數據丟失:

第一種丟失更新:

第一類丟失更新就是兩個事務同時更新一個數據,一個事務更新完畢並提交後,另一個事務回滾,造成提交的更新丟失。

 

事務A|事務B
-|-
開始事務|開始事務
讀取N=5|讀取N=5
修改N=8|修改N=9
 |提交,結束事務
回滾|
結束事務(N=5)|N=5,N本應爲提交的9

 

第二類丟失更新:

第二類丟失更新就是兩個事務同時更新一個數據,先更新的事務提交的數據會被後更新的事務提交的數據覆蓋,即先更新的事務提交的數據丟失。

 

事務A|事務B

-|-

開始事務|開始事務

讀取M=8|讀取M=8

更新M=1|

提交事務,M=1,結束|

 |更新M=7

   |提交事務,M=7,事務A的更新丟失,結束

 

  3. 幻讀(側重點在讀取到了新增或刪除的數據,數據條數對比)

指一個線程中的事務讀取到了另外一個線程中事務已提交的insert的數據。即一個事務A兩次或多次讀取數據,在此期間,事務B新增了N條數據,然後提交了,造成了事務A兩次或多次讀取數據出現了數據條數不一致的情況。

 

 

 

4.隔離級別-------級別越高,數據越安全,但性能越低

    讀數據一致性及允許的並   發副作用

隔離級別

讀數據一致性

髒讀

不可重複讀

幻讀

未提交讀(Read uncommitted)

最低級別,只能保證不讀取物理上損壞的數據

可能

可能

可能

已提交讀(Read committed)

語句級

不可能

可能

可能

可重複讀(Repeatable read)

事務級

不可能

不可能

可能

可串行化(Serializable)

最高級別,事務級

不可能

不可能

不可能

 

MySQL 默認的級別是:Repeatable read 可重複讀,其他主流數據庫,如Oracle;SQLServer默認級別爲:Read committed

 

     1) 未提交讀:寫事務阻止其他寫事務,避免了更新遺失。但是沒有阻止其他讀事務。

存在的問題:髒讀。即讀取到不一致的數據,因爲另一個事務可能還沒提交最終數據,這個讀事務就讀取了中途的數據,這個數據可能是不正確的。

解決辦法:已提交讀

     2) 已提交讀:寫事務會阻止其他讀寫事務。讀事務不會阻止其他任何事務。

存在的問題:不可重複讀。即在一次事務之間,進行了兩次讀取,但是結果不一致,可能第一次id爲1的人叫“張三”,第二次讀id爲1的人就叫“王五”了。因爲讀取操作不會阻止其他事務。

解決辦法:可重複讀

     3) 可重複讀

讀事務會阻止其他寫事務,但是不會阻止其他讀事務。

存在的問題:幻讀。可重複讀阻止的寫事務包括update和delete(只給存在的行加上了鎖),但是不包括insert(新行不存在,所以沒有辦法加鎖),所以一個事務第一次讀取可能讀取到了10條記錄,但是第二次可能讀取到11條,這就是幻讀。

解決辦法:串行化

     4) 可串行化

可避免幻讀。讀加共享鎖,寫加排他鎖。這樣讀取事務可以併發,但是讀寫,寫寫事務之間都是互斥的,基本上就是一個個執行事務,所以叫串行化。

 

5. 封鎖協議

封鎖協議就是在用X鎖或S鎖時制定的一些規則,比如鎖的持續時間,鎖的加鎖時間等。不同的封鎖協議對應不同的隔離級別。事務的隔離級別一共有4種,由低到高分別是Read uncommitted、Read committed、Repeatable read、Serializable,分別對應的相應的封鎖協議等級。

     1) 一級封鎖協議

一級封鎖協議對應的是Read uncommitted隔離級別,Read uncommitted讀未提交,一個事務可以讀取另一個事務未提交的數據,這是最低的級別。一級封鎖協議本質上是在事務修改數據之前加上X鎖,直到事務結束後才釋放,事務結束包括正常結束(commit)與非正常結束(rollback)。

 

      2) 二級封鎖協議

二級封鎖協議本質上在一級協議的基礎上(在修改數據時加X鎖),在讀數據時加上S鎖,讀完後立即釋放S鎖,可以避免髒讀。但有可能出現不可重複讀與幻讀。二級封鎖協議對應的是Read committed與Repeatable Read隔離級別。

 

      3) 三級封鎖協議

三級封鎖協議,在一級封鎖協議的基礎上(修改時加X鎖),讀數據時加上S鎖(與二級類似),但是直到事務結束後才釋放S鎖,可以避免幻讀,髒讀與不可重複讀。三級封鎖協議對應的隔離級別是Serializable。

 

 

6. 鎖機制

        1) 共享/排它鎖(Shared and Exclusive Locks)

共享鎖(Share Locks,記爲S鎖),讀取數據時加S鎖,共享鎖之間不互斥,即讀讀可以並行。

排他鎖(exclusive Locks,記爲X鎖),修改數據時加X鎖,排他鎖與任何鎖互斥,即寫讀,寫寫不可以並行。

一旦寫數據的任務沒有完成,數據是不能被其他任務讀取的,這對併發度有較大的影響。對應到數據庫,可以理解爲,寫事務沒有提交,讀相關數據的select也會被阻塞,這裏的select是指加了鎖的,普通的select仍然可以讀到數據(快照讀)。

 

        2) 意向鎖(Intention Locks)

InnoDB爲了支持多粒度鎖機制(multiple granularity locking),即允許行級鎖與表級鎖共存,而引入了意向鎖(intention locks)。意向鎖是指,未來的某個時刻,事務可能要加共享/排它鎖了,先提前聲明一個意向。

意向鎖是一個表級別的鎖(table-level locking);

意向鎖又分爲:

意向共享鎖(intention shared lock, IS),它預示着,事務有意向對錶中的某些行加共享S鎖;

意向排它鎖(intention exclusive lock, IX),它預示着,事務有意向對錶中的某些行加排它X鎖;

   加鎖的語法爲:

select ... lock in share mode;  要設置IS鎖;

select ... for update;       要設置IX鎖;

事務要獲得某些行的S/X鎖,必須先獲得表對應的IS/IX鎖,意向鎖僅僅表明意向,意向鎖之間相互兼容,兼容互斥表如下:

 

IS

IX

IS

IX

 

 

 

 

雖然意向鎖之間互相兼容,但是它與共享鎖/排它鎖互斥,其兼容互斥表如下:

 

S

X

IS

IX

 

 

 

 

排它鎖是很強的鎖,不與其他類型的鎖兼容。這其實很好理解,修改和刪除某一行的時候,必須獲得強鎖,禁止這一行上的其他併發,以保障數據的一致性。

        3) 記錄鎖(Record Locks)-----鎖定某行

對單條索引記錄進行加鎖,鎖住的是索引記錄而非記錄本身,即使表中沒有任何索引,MySQL會自動創建一個隱式的row_id作爲聚集索引來進行加鎖。記錄鎖就是爲某行記錄加鎖,它封鎖該行的索引記錄。查詢條件必須爲主鍵列或唯一索引,否則鎖會變成臨建鎖。同時查詢語句爲精準匹配(=),不能爲>、<、like等,否則也會退化成臨建鎖。事務隔離級別是RR。

 

        4) 間隙鎖(Gap Locks) -----鎖定某個區間

間隙鎖,它封鎖索引記錄中的一段間隔範圍,或者第一條索引記錄之前的範圍,又或者最後一條索引記錄之後的範圍。

存儲引擎是InnoDB,事務隔離級別是RR。

間隙鎖的主要目的,就是爲了防止其他事務在間隔中插入數據,以導致“不可重複讀”。如果把事務的隔離級別降級爲已提交讀(Read Committed, RC),間隙鎖則會自動失效。例如:

Select * from test where id between 1 and 8 for update;

SQL語句會封鎖區間(1,8),以阻止其他事務插入id位於該區間的記錄。

 

        5) 臨鍵鎖(Next-key Locks) -----鎖定左開右閉的一段區間

臨鍵鎖,是記錄鎖與間隙鎖的組合,它的封鎖範圍,既包含索引記錄,又包含索引區間。

臨鍵鎖的主要目的,也是爲了避免幻讀。

如果把事務的隔離級別降級爲RC,臨鍵鎖就也會失效。

每個數據行上的非唯一索引列上都會存在一把臨鍵鎖,當某個事務持有該數據行的臨鍵鎖時,會鎖住一段左開右閉區間的數據。需要強調的一點是,InnoDB 中行級鎖是基於索引實現的,臨鍵鎖只與非唯一索引列有關,在唯一索引列(包括主鍵列)上不存在臨鍵鎖。

假設有如下表:
MySql,InnoDB,Repeatable-Read:table(id PK, age KEY, name)

id

age

name

1

10

Lee

3

24

Soraka

5

32

Zed

7

45

Talon

該表中 age 列潛在的臨鍵鎖有:
(-∞, 10],
(10, 24],
(24, 32],
(32, 45],
(45, +∞],

 

在根據非唯一索引 對記錄行進行 UPDATE \ FOR UPDATE \ LOCK IN SHARE MODE 操作時,InnoDB 會獲取該記錄行的 臨鍵鎖 ,並同時獲取該記錄行下一個區間的間隙鎖。

 

InnoDB 中的行鎖的實現依賴於索引,一旦某個加鎖操作沒有使用到索引,那麼該鎖就會退化爲表鎖。

記錄鎖存在於包括主鍵索引在內的唯一索引中,鎖定單條索引記錄。

間隙鎖存在於非唯一索引中,鎖定開區間範圍內的一段間隔,它是基於臨鍵鎖實現的。

臨鍵鎖存在於非唯一索引中,該類型的每條記錄的索引上都存在這種鎖,它是一種特殊的間隙鎖,鎖定一段左開右閉的索引區間。

 

      6) 插入意向鎖(Insert Intention Locks)

插入意向鎖,是間隙鎖(Gap Locks)的一種(所以,也是實施在索引上的),它是專門針對insert操作的。多個事務,在同一個索引,同一個範圍區間插入記錄時,如果插入的位置不衝突,不會阻塞彼此。

目的是提高插入併發。

       7) 自增鎖(Auto-inc Locks)

自增鎖是一種特殊的表級別鎖(table-level lock),專門針對事務插入AUTO_INCREMENT類型的列。如果一個事務正在往表中插入記錄,所有其他事務的插入必須等待,以便第一個事務插入的行,是連續的主鍵值。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章