本章主要講解MySQL是如何在併發請求保證資料的正確性。
引入了一些概念,指引學習方向,文末有我們學習參考文章
學習一個新鮮事物的時,我們總會碰到一些新的概念或設計,如果你細心領悟或許可以舉一反三,通過之前學過的東西與之關聯。但是如果你並無感悟,倒也無妨。因為大多數你只需要整理後自己能記住就可以。畢竟我們不是開創者而是使用者。接下來我將要介紹幾個概念或是設計是針對於今天所探討的主題。
MVCC (多版本併發控制)
對於未接觸過這個概念的人肯定是一臉霧水,那我只簡介一下它的作用。如果想學習可以自行查詢資料。
它會在每個資料行後面追加隱藏兩個欄位,分別代表建立(建立的事務版本號),刪除(刪除的事務版本號)。
事務版本號:每啟動一個事務都會產生版本號
id | name | create_version | delete_version |
---|---|---|---|
1 | dog | 1 | 2 |
1 | pig | 2 | null |
2 | cat | 2 | 3 |
3 | cats | 3 | null |
上表為MVCC的一個簡圖,我來描述一下它的經歷。
- 事務1 :插入一條id=1,name=dog的資料。
- 事務2:更新了id=1,name=pig。 並且插入一條資料id=2,name=cat
- 事務3:插入了一條id=3,name=cats,並且刪除了id=2的資料
有了這個設計我們就能弄出不同版本的查詢結果(隔離級別中的快照讀就與之相識)
- 獲取最新版本(版本3)的資料,查詢條件為:create_version>=3 and delete_version=null。
- 獲取指定版本(版本2)的資料,查詢條件為:create_version<=2 and (delete_version=null or delete_version>2)。
鎖 (防止資料併發操作)
鎖一個特別簡單的名詞,但是初學者未必能理的清楚概念。像我這樣文字理解能不好的人就卡了半天。
最後我的理解是:阻礙你獲取資料的就是鎖。鎖是你與資料交流間的一道屏障。
行鎖
共享鎖(S鎖)又稱為讀鎖(Share lock,簡記為S鎖),若事務T對資料物件A加上S鎖,則其它事務只能再對A加S鎖,而不能加X鎖,直到T釋放A上的S鎖。
排它鎖(X鎖)又稱為寫鎖((eXclusive lock,簡記為X鎖)),若事務T對資料物件A加上X鎖,則只允許T讀取和修改A,其它任何事務都不能再對A加任何型別的鎖,直到T釋放A上的鎖。它防止任何其它事務獲取資源上的鎖,直到在事務的末尾將資源上的原始鎖釋放為止。
這裡我進行了一個比喻,假設資料是一個透明的箱子。如果箱子裡東西能看到就是可查詢,能觸碰到就是可修改。
共享鎖:新增條件(能看到箱子裡東西的事務都能新增,否者需要等待),相當於給箱子加了一個鎖,這個鎖只有自己能開啟。
排它鎖:新增條件(能觸碰到箱子裡東西的事務都能新增,否者需要等待),相當於開啟箱子給裡面東西套上一個盒子,再對盒子加鎖,這個鎖只有自己能開啟
- 事務1 為資料新增共享鎖,事務2能再新增共享鎖嗎?如果能,事務2能修改資料嗎,事務1能修改資料嗎?
- 事務1 為資料新增共享鎖,事務2能新增排它鎖嗎?如果能,事務2能修改資料嗎,事務1能修改資料嗎?
- 事務1 為資料新增排它鎖,事務2能新增共享鎖嗎?如果能,事務2能修改資料嗎,事務1能修改資料嗎?
- 事務1 為資料新增排它鎖,事務2能新增排它鎖嗎?如果能,事務2能修改資料嗎,事務1能修改資料嗎?
間隙鎖 (gap鎖)
會在查詢資料的兩側插入間隙鎖,導致無法插入。 這與InnoDB索引順序排序有關。
四種事務隔離
在資料庫操作中,為了有效保證併發讀取資料的正確性,提出的事務隔離級別。我們的資料庫鎖,也是為了構建這些隔離級別存在的。
鎖和多版本資料(MVCC)是 InnoDB 實現一致性讀和隔離級別的手段。
因此,在不同的隔離級別下,InnoDB 處理 SQL 時採用的一致性讀策略和需要的鎖是不同的。
Read Uncommitted (RU)
在該隔離級別,所有事務都可以看到其他未提交事務的執行結果。本隔離級別很少用於實際應用,因為它的效能也不比其他級別好多少。讀取未提交的資料,也被稱之為髒讀(Dirty Read)。
Read Committed (RC)
這是大多數資料庫系統的預設隔離級別(但不是MySQL預設的)。它滿足了隔離的簡單定義:一個事務只能看見已經提交事務所做的改變。這種隔離級別 也支援所謂的不可重複讀(Nonrepeatable Read),因為同一事務的其他例項在該例項處理其間可能會有新的commit,所以同一select可能返回不同結果。
Repeatable Read (RR)
這是MySQL的預設事務隔離級別,它確保同一事務的多個例項在併發讀取資料時,會看到同樣的資料行。不過理論上,這會導致另一個棘手的問題:幻讀 (Phantom Read)。簡單的說,幻讀指當使用者讀取某一範圍的資料行時,另一個事務又在該範圍內插入了新行,當使用者再讀取該範圍的資料行時,會發現有新的“幻影” 行。InnoDB和Falcon儲存引擎通過多版本併發控制(MVCC,Multiversion Concurrency Control)機制解決了該問題。
Serializable(可序列化)
這是最高的隔離級別,它通過強制事務排序,使之不可能相互衝突,從而解決幻讀問題。簡言之,它是在每個讀的資料行上加上共享鎖。在這個級別,可能導致大量的超時現象和鎖競爭。
事務中sql語句有加鎖與不加鎖兩種,我們把不加鎖的定義為快照讀,加鎖的定義為當前讀,以下是分類
- 快照讀:就是select
- select * from table ….;
- 當前讀:特殊的讀操作,插入/更新/刪除操作,屬於當前讀,處理的都是當前的資料,需要加鎖。
- select * from table where ? lock in share mode; S鎖,Gap鎖
- select * from table where ? for update; X鎖,Gap鎖
- insert; X鎖,Gap鎖
- update ; X鎖,Gap鎖
- delete; X鎖,Gap鎖
MVCC定義了隔離級別不同的快照讀版本,只對RC與RR進行分析。
- RC隔離級別:每次快照讀都是獲取最新版本。
- RR隔離級別:每次快照讀都是獲取指定版本。
不同的隔離級別當前讀加鎖情況不同,只對RC與RR進行分析。
- 共同點:在新增X鎖與S鎖方面是一樣的。
- 不同點:RR隔離級別有時還會新增Gap鎖。
加鎖分析
資料庫加鎖解鎖時間點
資料庫遵循的是兩段鎖協議,將事務分成兩個階段,加鎖階段和解鎖階段(所以叫兩段鎖)
事務 | 加鎖/解鎖處理 |
---|---|
begin | |
insert into test ….. | 加insert對應的鎖 |
update test set… | 加update對應的鎖 |
delete from test …. | 加delete對應的鎖 |
delete from test …. | 事務提交時,同時釋放insert、update、delete對應的鎖 |
但在實際使用過程當中,MySQL做了一些改進,在MySQL Server過濾條件,發現不滿足後,會呼叫unlock_row方法,把不滿足條件的記錄釋放鎖 (違背了二段鎖協議的約束)。這樣做,保證了最後只會持有滿足條件記錄上的鎖,但是每條記錄的加鎖操作還是不能省略的。可見即使是MySQL,為了效率也是會違反規範的。(參見《高效能MySQL》中文第三版p181)
資料庫如何加鎖
資料庫加鎖是比較複雜,有比較多的情況。需要你瞭解InnoDB的索引機制。這裡簡單通過sql執行計劃(explain)分析,步驟如下:
- type=range
- key=PRIMARY:主鍵的範圍掃描,會在主鍵B+樹新增行鎖。RR隔離級別下主鍵B+樹會新增間隙鎖。
- key=唯一索引:唯一索引的範圍掃描,會在主鍵B+樹新增行鎖,唯一索引B+樹上新增行鎖。RR隔離級別下唯一索引B+樹會新增間隙鎖。
- key=普通索引:普通索引的範圍掃描,會在主鍵B+樹新增行鎖,普通索引B+樹上新增行鎖。RR隔離級別下普通索引B+樹會新增間隙鎖。
- type=ref
- key=普通索引:普通索引掃描,返回匹配某個單獨值的所有行,會在主鍵B+樹新增行鎖,普通索引B+樹上新增行鎖。RR隔離級別下普通索引B+樹會新增間隙鎖。
- type=eq_ref
- key=唯一索引:唯一索引掃描,返回匹配某個單獨值行,會在主鍵B+樹新增行鎖,唯一索引B+樹上新增行鎖。
- type=const
- key=主鍵索引:主鍵索引掃描,返回匹配某個單獨值行,會在主鍵B+樹新增行鎖。
以下是作者的理解,符合所學知識,大家可以參考。
- 是否可重複讀是針對於快照讀,幻讀是針對於當前讀。
- RC的不可重複讀,是因為在RC隔離中快照讀是獲取最新快照版本,所有每次相同sql快照讀的資料都可能不一樣。
- RR可重複讀,是因為在RR隔離中快照讀是獲取指定快照版本,所有每次相同sql快照讀資料的是一樣的。
-
RC隔離中存在幻讀可能性。舉個例子有一個獲取某表全部資訊sql當前讀(select from table for update)(sql的語義是鎖住全表並獲取最新資訊),但是這時如果另一個事務執行(insert into table) 語句仍然可以插入,因為RC隔離中沒有間隙鎖。這會導致前面select from table for update 的資料不是最新有效資料。這就是幻讀。
幻讀——一個當前讀的查詢sql但是保證不了資料的正確性,這個查詢就可能存在幻讀。
- RR隔離中有間隙鎖,所以不會幻讀。資料上會出現幻讀的其實已經被InnoDB引擎優化了。
真正理解Mysql的四種隔離級別
MySQL鎖總結
MYSQL MVCC實現原理
理解事務 - MySQL 事務處理機制
Innodb中的事務隔離級別和鎖的關係
MySQL 加鎖處理分析