[轉(zhuǎn)載]技術(shù)分享 | 我對 MySQL 隔離級別的剖析以及了解整個數(shù)據(jù)范圍的全集組成。

關(guān)于間隙鎖這篇文章講得太好了。
原文地址:https://mp.weixin.qq.com/s/DhMy6fsdlFj3dGqRE_0JMg
術(shù)式之后皆為邏輯,一切皆為需求和實(shí)現(xiàn)。希望此文能從需求、現(xiàn)狀和解決方式的角度幫大家理解隔離級別。

隔離級別的產(chǎn)生

在串型執(zhí)行的條件下,數(shù)據(jù)修改的順序是固定的、可預(yù)期的結(jié)果,但是并發(fā)執(zhí)行的情況下,數(shù)據(jù)的修改是不可預(yù)期的,也不固定,為了實(shí)現(xiàn)數(shù)據(jù)修改在并發(fā)執(zhí)行的情況下得到一個固定、可預(yù)期的結(jié)果,由此產(chǎn)生了隔離級別。

所以隔離級別的作用是用來平衡數(shù)據(jù)庫并發(fā)訪問與數(shù)據(jù)一致性的方法。

事務(wù)的4種隔離級別

image.png

在了解了 4 種隔離級別的需求后,在采用鎖控制隔離級別的基礎(chǔ)上,我們需要了解加鎖的對象(數(shù)據(jù)本身&間隙),

數(shù)據(jù)范圍全集組成

SQL 語句根據(jù)條件判斷不需要掃描的數(shù)據(jù)范圍(不加鎖);

SQL 語句根據(jù)條件掃描到的可能需要加鎖的數(shù)據(jù)范圍;

以單個數(shù)據(jù)范圍為例,數(shù)據(jù)范圍全集包含:(數(shù)據(jù)范圍不一定是連續(xù)的值,也可能是間隔的值組成)

  1. 數(shù)據(jù)已經(jīng)填充了整個數(shù)據(jù)范圍:(被完全填充的數(shù)據(jù)范圍,不存在數(shù)據(jù)間隙)
  • 整形,對值具有唯一約束條件的數(shù)據(jù)范圍 1~5 ,
    已有數(shù)據(jù)1、2、3、4、5,此時數(shù)據(jù)范圍已被完全填充;
  • 整形,對值具有唯一約束條件的數(shù)據(jù)范圍 1 和 5 ,
    已有數(shù)據(jù)1、5,此時數(shù)據(jù)范圍已被完全填充;
  1. 數(shù)據(jù)填充了部分?jǐn)?shù)據(jù)范圍:(未被完全填充的數(shù)據(jù)范圍,是存在數(shù)據(jù)間隙)
  • 整形的數(shù)據(jù)范圍 1~5 ,
    已有數(shù)據(jù) 1、2、3、4、5,但是因為沒有唯一約束,
    所以數(shù)據(jù)范圍可以繼續(xù)被 1~5 的數(shù)據(jù)重復(fù)填充;
  • 整形,具有唯一約束條件的數(shù)據(jù)范圍 1~5 ,
    已有數(shù)據(jù) 2,5,此時數(shù)據(jù)范圍未被完全填充,還可以填充 1、3、4 ;
  1. 數(shù)據(jù)范圍內(nèi)沒有任何數(shù)據(jù)(存在間隙)
    如下:
    -整形的數(shù)據(jù)范圍 1~5 ,數(shù)據(jù)范圍內(nèi)當(dāng)前沒有任何數(shù)據(jù)。
    在了解了數(shù)據(jù)全集的組成后,我們再來看看事務(wù)并發(fā)時,會帶來的問題。

無控制的并發(fā)所帶來的問題

并發(fā)事務(wù)如果不加以控制的話會帶來一些問題,主要包括以下幾種情況。

  1. 范圍內(nèi)已有數(shù)據(jù)更改導(dǎo)致的:
  • 更新丟失:當(dāng)多個事務(wù)選擇了同一行,然后基于最初選定的值更新該行時,
    由于每個事物不知道其他事務(wù)的存在,最后的更新就會覆蓋其他事務(wù)所做的更新;

  • 臟讀: 一個事務(wù)正在對一條記錄做修改,這個事務(wù)完成并提交前,這條記錄就處于不一致狀態(tài)。
    這時,另外一個事務(wù)也來讀取同一條記錄,如果不加控制,
    第二個事務(wù)讀取了這些“臟”數(shù)據(jù),并據(jù)此做了進(jìn)一步的處理,就會產(chǎn)生提交的數(shù)據(jù)依賴關(guān)系。
    這種現(xiàn)象就叫“臟讀”。

  1. 范圍內(nèi)數(shù)據(jù)量發(fā)生了變化導(dǎo)致:
  • 不可重復(fù)讀:一個事務(wù)在讀取某些數(shù)據(jù)后的某個時間,再次讀取以前讀過的數(shù)據(jù),
    卻發(fā)現(xiàn)其讀出的數(shù)據(jù)已經(jīng)發(fā)生了改變,或者某些記錄已經(jīng)被刪除了。
    這種現(xiàn)象就叫“不可重復(fù)讀”。

  • 幻讀:一個事務(wù)按相同的查詢條件重新讀取以前檢索過的數(shù)據(jù),
    卻發(fā)現(xiàn)其他事務(wù)插入了滿足其查詢條件的新數(shù)據(jù),這種現(xiàn)象稱為“幻讀”。
    可以簡單的認(rèn)為滿足條件的數(shù)據(jù)量變化了。

因為無控制的并發(fā)會帶來一系列的問題,這些問題會導(dǎo)致無法滿足我們所需要的結(jié)果。
因此我們需要控制并發(fā),以實(shí)現(xiàn)我們所期望的結(jié)果(隔離級別)。

MySQL 隔離級別的實(shí)現(xiàn)

InnoDB 通過加鎖的策略來支持這些隔離級別。

行鎖包含:

  • Record Locks
    索引記錄鎖,索引記錄鎖始終鎖定索引記錄,即使表中未定義索引,
    這種情況下,InnoDB 創(chuàng)建一個隱藏的聚簇索引,并使用該索引進(jìn)行記錄鎖定。

  • Gap Locks
    間隙鎖是索引記錄之間的間隙上的鎖,或者對第一條記錄之前或者最后一條記錄之后的鎖。
    間隙鎖是性能和并發(fā)之間權(quán)衡的一部分。
    對于無間隙的數(shù)據(jù)范圍不需要間隙鎖,因為沒有間隙。

  • Next-Key Locks
    索引記錄上的記錄鎖和索引記錄之前的 gap lock 的組合。
    假設(shè)索引包含 10、11、13 和 20。
    可能的next-key locks包括以下間隔,其中圓括號表示不包含間隔端點(diǎn),方括號表示包含端點(diǎn):


    image.png

"上確界"偽記錄的值高于索引中任何實(shí)際值。

上確界不是一個真正的索引記錄,因此,實(shí)際上,這個 next-key 只鎖定最大索引值之后的間隙。

基于此,當(dāng)獲取的數(shù)據(jù)范圍中,數(shù)據(jù)已填充了所有的數(shù)據(jù)范圍,那么此時是不存在間隙的,也就不需要 gap lock。

對于數(shù)據(jù)范圍內(nèi)存在間隙的,需要根據(jù)隔離級別確認(rèn)是否對間隙加鎖。

默認(rèn)的 REPEATABLE READ 隔離級別,為了保證可重復(fù)讀,除了對數(shù)據(jù)本身加鎖以外,還需要對數(shù)據(jù)間隙加鎖。

READ COMMITTED 已提交讀,不匹配行的記錄鎖在 MySQL 評估了 where 條件后釋放。

對于 update 語句,InnoDB 執(zhí)行 "semi-consistent" 讀取,這樣它會將最新提交的版本返回到 MySQL,

以便 MySQL 可以確定該行是否與 update 的 where 條件相匹配。

現(xiàn)在我們來驗證以下 MySQL 對于隔離級別的實(shí)現(xiàn)是否符合預(yù)期。

場景演示

image.png
image.png

總結(jié)&延展:

唯一索引存在唯一約束,所以變更后的數(shù)據(jù)若違反了唯一約束的原則,則會失敗。

當(dāng) where 條件使用二級索引篩選數(shù)據(jù)時,會對二級索引命中的條目和對應(yīng)的聚簇索引都加鎖;所以其他事務(wù)變更命中加鎖的聚簇索引時,都會等待鎖。

行鎖的增加是一行一行增加的,所以可能導(dǎo)致并發(fā)情況下死鎖的發(fā)生。

例如,在 session A 對符合條件的某聚簇索引加鎖時,可能 session B 已持有該聚簇索引的 Record Locks,而 session B 正在等待 session A 已持有的某聚簇索引的 Record Locks。
session A 和 session B 是通過兩個不相干的二級索引定位到的聚簇索引。
session A 通過索引 idA,session B通過索引 idB 。

當(dāng) where 條件獲取的數(shù)據(jù)無間隙時,無論隔離級別為 rc 或 rr,都不會存在間隙鎖。
比如通過唯一索引獲取到了已完全填充的數(shù)據(jù)范圍,此時不需要間隙鎖。

間隙鎖的目的在于阻止數(shù)據(jù)插入間隙,所以無論是通過 insert 或 update 變更導(dǎo)致的間隙內(nèi)數(shù)據(jù)的存在,都會被阻止。

rc 隔離級別模式下,查詢和索引掃描將禁用 gap locking,此時 gap locking 僅用于外鍵約束檢查和重復(fù)鍵檢查(主要是唯一性檢查)。

rr 模式下,為了防止幻讀,會加上 Gap Locks。

事務(wù)中,SQL 開始則加鎖,事務(wù)結(jié)束才釋放鎖。
就鎖類型而言,應(yīng)該有優(yōu)化鎖,鎖升級等,例如rr模式未使用索引查詢的情況下,是否可以直接升級為表鎖。
就鎖的應(yīng)用場景而言,在回放場景中,如果確定事務(wù)可并發(fā),則可以考慮不加鎖,加快回放速度。

鎖只是并發(fā)控制的一種粒度,只是一個很小的部分:
從不同場景下是否需要控制并發(fā),(已知無交集且有序的數(shù)據(jù)的變更,MySQL 的 MTS 相同前置事務(wù)的多事務(wù)并發(fā)回放)
并發(fā)控制的粒度,(鎖是一種邏輯粒度,可能還存在物理層和其他邏輯粒度或方式)
相同粒度下的優(yōu)化,(鎖本身存在優(yōu)化,如IX、IS類型的優(yōu)化鎖)
粒度加載的安全&性能(如獲取行鎖前,先獲取頁鎖,頁鎖在執(zhí)行獲取行鎖操作后即釋放,無論是否獲取成功)等多個層次去思考并發(fā)這玩意。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1. 什么是MVCC? 多版本并發(fā)控制(Multi-Version Concurrency Control, MV...
    SHAN某人閱讀 5,543評論 0 25
  • 1.鎖 表鎖(myisam手動增加) 總而言之,就是讀鎖會阻塞寫,但是不會阻塞讀。而寫鎖會把讀和寫都堵塞。 查看表...
    _不想翻身的咸魚閱讀 600評論 0 0
  • 我們都知道事務(wù)的幾種性質(zhì),數(shù)據(jù)庫為了維護(hù)這些性質(zhì),尤其是一致性和隔離性,一般使用加鎖這種方式。同時數(shù)據(jù)庫又是個高并...
    CodeKing2017閱讀 719評論 0 7
  • 表情是什么,我認(rèn)為表情就是表現(xiàn)出來的情緒。表情可以傳達(dá)很多信息。高興了當(dāng)然就笑了,難過就哭了。兩者是相互影響密不可...
    Persistenc_6aea閱讀 129,836評論 2 7
  • 16宿命:用概率思維提高你的勝算 以前的我是風(fēng)險厭惡者,不喜歡去冒險,但是人生放棄了冒險,也就放棄了無數(shù)的可能。 ...
    yichen大刀閱讀 8,085評論 0 4

友情鏈接更多精彩內(nèi)容