MySQL45學習幻讀

1.什麼是幻讀

幻讀指的是一個事務在前後兩次查詢同一個範圍的時候,後一次查詢看到了前一次查詢沒有看到的行

在可重複讀隔離級別下,普通的查詢是快照讀,是不會看到別的事務插入的數據的。因此,幻讀在“當前讀”下才會出現。

幻讀僅專指“新插入的行”

幻讀的例子:

  • 假設有一個幻讀的情況:
  • 表 T(id,a) 裏面只有一行數據 row(id=1,a=1,b=1)
時間順序 事務1 事務2
time1 select * from T where a = 1 for update (id=1,a=1)
time2 什麼都不做 insert(id=2,a=1);
time3 select * from T where a = 1 for update (id=1,a=1) (id=2,a=1)
  • time1的查詢結果是(id=1,a=1)
  • time3的查詢結果是(id=1,a=1),(id=2,a=1)
  • 同一個事務之中兩次查詢的結果不一致

2. 爲什麼要防止幻讀

If we regard a set of rows as a data item, the new phantom child would violate the isolation principle of transactions that a transaction should be able to run so that the data it has read does not change during the transaction.

因爲幻讀違背了事務隔離原則
例子中的情況,按道理來說應該在a=1的數據上加鎖,但是這裏
事務在執行期間,事務讀取到的數據本應該是不會變化的

3. 怎麼幻讀是怎麼解決的

MySQL使用臨鍵鎖來解決幻讀的問題,MySQL鎖介紹

  • 臨鍵鎖 = 記錄鎖 + 間隙鎖
  • 假設有個事務T1,進行了查詢(當強讀就是select ... for update)操作
  • MySQL會根據where條件,掃描索引(如果沒有普通索引則掃描聚簇索引)
  • 掃描過的索引範圍上加上記錄鎖間隙鎖
  • 其他事務想要插入,得在插入操作之前加鎖
  • 但是因爲where條件掃描過的範圍上已經被加上了間隙鎖記錄鎖,所以insert操作會被阻塞
  • 必須等到事務T1釋放鎖,才能執行成功

例子

  • 設有表t,表中只有三列(id,a,b),id是主鍵,a上有索引
  • 表裏有2條數據(id=1,a=1,b=1)(id=10,a=10,b=10)

例1:

T1:
start transaction;
select * from t where a > 5 for update;
  • 當前讀語句會在a的索引上加三個鎖
    1. 間隙鎖:(1,10)開區間
    1. 記錄鎖:a=10
    1. 間隙鎖:(10,supremum)開區間

例2:

T1:
start transaction;
select * from t where a = 10 for update;
  • 當前讀語句會在a的索引上加三個鎖
    1. 間隙鎖:(1,10)開區間
    1. 記錄鎖:a=10
    1. 間隙鎖:(10,supremum)開區間

例3:

T1:
start transaction;
select * from t where b = 10 for update;
  • 當前讀語句會在id聚簇索引上加5個鎖
    1. 間隙鎖:(infimum,1)開區間
    1. 記錄鎖:id=1
    1. 間隙鎖:(1,10)開區間
    1. 記錄鎖:id=10
    1. 間隙鎖:(10,supremum)開區間
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章