mysql中的各類鎖把我搞糊塗啦~

你們好, 我是公衆號:java小杰要加油
今天來分享一個關於mysql的知識點—— mysql中的鎖
  • 話很少說,直接開車

事務併發訪問狀況

讀-讀 狀況

  • 併發事務讀取相同的數據,並不會對數據形成影響,容許併發讀

寫-寫 狀況

  • 多事務併發寫寫時會發生髒寫的狀況,不過任何一個事務隔離級別都不容許此狀況發生,經過加鎖來杜絕髒寫

髒寫

  • 事務T1 將數據改爲了A,可是還未提交,可此時事務T2又將數據改爲了B,覆蓋了事務T1的更改,T1更新丟失,這種狀況叫作髒寫

加鎖

  • 例如,如今事務T1,T2對這條記錄進行併發更改,剛纔說是隔離級別是經過加鎖來杜絕此髒寫的,流程以下


這個鎖結構中有兩個比較關鍵的信息(其實還有不少信息,後面再聊)java

  • trx信息:表示這個鎖結構是和哪一個事務所關聯的
  • is_waiting信息:表示當前事務是否正在等待
Q: 能描述一下兩個事務併發修改同一條數據時,mysql這個鎖是怎麼避免髒寫的嗎?

A :事務T1在更改這條數據前,就先內存中生成一把鎖與此數據相關聯(is_waiting爲false,表明沒有等待),而後咔咔一頓操做更改數據,這個時候,事務T2來了,發現此記錄已經有一把鎖與之相關聯了(就是T1那一把鎖),而後就開始等待(is_waiting爲true表明正在等待),事務T1更改完數據提交事務後,就會把此事務對應的所結構釋放掉,而後檢測一下還有沒有與此記錄相關聯的鎖,結果發現T2還在苦苦的等待,就把T2的鎖結構的(is_waiting爲false,表明沒有等待)而後把T2事務對應的線程喚醒,T2獲取鎖成功繼續執行,整體流程如上。mysql

讀-寫 /寫-讀 狀況

在讀-寫 / 寫 -讀的狀況下會出現髒讀,不可重複讀,幻讀的現象,不一樣的隔離級別能夠避免不一樣的問題,具體相關內容能夠看小杰的這篇文章 京東面試官問我:「聊聊MySql事務,MVCC?」 面試

不過貼心的我仍是列出來了 注:√表明可能發生,×表明不可能發生sql

隔離級別 髒讀 不可重複讀 幻讀
讀未提交(read uncommitted RU)
讀提交(read committed RC) ×
可重複讀(repeatable read RR) × ×
串行化(serializable ) × × ×

可是 RR在某些程度上避免了幻讀的發生併發

怎麼避免髒讀、不可重複讀、幻讀這些現象呢?其實有兩種方案mvc

  • 方案一 :讀操做使用MVCC寫操做進行加鎖
  • mvcc裏面最重要的莫過於ReadView了,它的存在保證了事務不能夠讀取到未提交的事務所做的更改,避免了髒讀。
  • 在RC隔離級別下,每次select讀操做都會生成ReadView
  • 在RR隔離級別下,只有第一次select讀操做纔會生成ReadView,以後的select讀操做都複用這一個ReadView
  • 方案二:讀寫操做都用加鎖
某些業務場景不容許讀取舊記錄的值,每次讀取都要讀取最新的值。
例如銀行取款事務中,先把餘額讀取出來,再對餘額進行操做。當這個事務在讀取餘額時,不容許其餘事務對此餘額進行訪問讀取,直到取款事務結束後才能夠訪問餘額。因此在讀數據的時候也要加鎖

鎖分類

當使用 讀寫都加鎖這個方案來避免併發事務 寫-寫讀-寫寫-讀時而產生的 髒讀不可重複讀幻讀現象時,那麼這個鎖它就要作到,讀讀時不相互影響,上面三種狀況時要相互阻塞,這時鎖也分了好幾類,咱們繼續往下看

鎖定讀

  • 共享鎖(Shared Lock):簡稱S鎖,在事務要讀取一條記錄時,須要先獲取該記錄的S鎖
  • 獨佔鎖(Exclusive Lock):簡稱X鎖,也稱排他鎖,在事務要改動一條記錄時,須要先獲取該記錄的X鎖

他們之間兼容關係以下 √表明能夠兼容,×表明不可兼容jvm

兼容性 S鎖 X鎖
S鎖 ×
X鎖 × ×

事務T1獲取某記錄的S鎖後,

  • 事務T2也能夠獲取此記錄的S鎖,(兼容)
  • 事務T2不能夠獲取此記錄的X鎖,直到T1提交後將S鎖釋放 (不兼容)

事務T1獲取某記錄的X鎖後,

  • 事務T2不能夠獲取此記錄的S鎖,直到T1提交後將X鎖釋放 (不兼容)
  • 事務T2不能夠獲取此記錄的X鎖,直到T1提交後將X鎖釋放 (不兼容)

鎖定讀語句

SELECT .. LOCK IN SHARE MODE   # 對讀取的記錄添加S鎖

SELECT .. FOR UPDATE # 對讀取的記錄添加X鎖

多粒度鎖

前面提到的鎖都是針對記錄的,其實一個事務也能夠在表級進行加鎖(S鎖、X鎖)ui

  • T1給表加了S鎖,那麼

    • T2能夠繼續獲取此表的S鎖
    • T2能夠繼續獲取此表中的某些記錄的S鎖
    • T2不能夠繼續獲取此表的X鎖
    • T2不能夠繼續獲取此表中的某些記錄的X鎖
  • T1給表加了X鎖,那麼

    • T2不能夠繼續獲取此表的S鎖
    • T2不能夠繼續獲取此表中的某些記錄的S鎖
    • T2不能夠繼續獲取此表的X鎖
    • T2不能夠繼續獲取此表中的某些記錄的X鎖

但是怎麼可能無緣無故的就給表加鎖呢,難道沒什麼條件嗎?答案是確定有條件的spa

  • 若想給表加S鎖,得先確保表中記錄沒有X鎖
  • 若想給表加X鎖,得先確保表中記錄沒有X鎖和S鎖

可是這個怎麼確保呢?難道要一行一行的遍歷表中的全部數據嗎?固然不是啦,聰明的大佬們想出了下面這兩把鎖線程

  • 意向共享鎖(Intention Shared Lock):簡稱IS鎖,當事務準備在某記錄上加S鎖時,須要先在表級別加上一個IS鎖
  • 意向獨佔鎖(Intention Exclusive Lock):簡稱IX鎖,當事務準備在某記錄上加X鎖時,須要先在表級別加上一個IX鎖

讓咱們來看下加上這兩把鎖以後的效果是什麼樣子的

  • 當想給記錄加S鎖時,先給表加一個IS鎖,而後再給記錄加S鎖

  • 當想給記錄加X鎖時,先給表加IX鎖,而後再給記錄加X鎖

而後 通過上面的操做以後

  • 若是想給表加S鎖,先看下錶加沒加IX鎖,若是有的話,則代表此表中的記錄有X鎖,則須要等到IX鎖釋放掉後才能夠加S鎖

  • 若是想給表加X鎖,先看下錶加沒加IS鎖或者IX鎖,若是有的話,則代表此表中的記錄有S鎖或者X鎖,則須要等到IS鎖或者IX鎖釋放掉後才能夠加X鎖

這幾種鎖的兼容性以下表

兼容性 IS鎖(表級鎖) S鎖 IX鎖(表級鎖) X鎖
IS鎖(表級鎖) ×
S鎖 × ×
IX鎖(表級鎖) × ×
X鎖 × × × ×
  • IS、IX鎖都是表級鎖,他們能夠共存。
  • 他們的提出僅僅是爲了在以後加表級別的S鎖或者X鎖時能夠快速判斷表中的記錄是否被上鎖,避免用遍歷的方式來查看一行一行的去查看而已

InnoDB中的行級鎖

Record Lock(記錄鎖)

  • 官方名字 LOCK_REC_NOT_GAP
  • 僅僅鎖住一條記錄
  • 有S型和X型之分

Gap Lock(間隙鎖)

  • 官方名字 LOCK_GAP
  • 給某記錄加此鎖後,阻塞數據在此記錄和上一個記錄的間隙插入,可是不鎖定此記錄
  • 有S型和X型之分,但是並無什麼區別他們的做用是相同的,gap鎖的做用僅僅是爲了防止插入幻影記錄而已,若是對一條記錄加了gap鎖(不管S/X型)並不會限制其餘事務對這條記錄加Record Lock或者Gap Lock

Next-Key Lock(記錄鎖+間隙鎖)

  • 官方名字 LOCK_ORDINARY
  • 既能夠鎖住某條記錄,又能夠組織其餘事務在該記錄面前插入新記錄

Insert Intention Lock(插入意向鎖鎖)

  • 官方名字 LOCK_INSERT_INTENTION
  • 事務在插入記錄時,若是插入的地方加了gap鎖,那麼此事務須要等待,此時此事務在等待時也須要生成一個鎖結構,就是插入意向鎖

鎖內存結構

  • 咱們難道鎖一條記錄就要生成一個鎖結構嗎?

固然不是!

一個鎖結構

若是被加鎖的記錄符合下面四條狀態的話,那麼這些記錄的鎖則會合到一個鎖結構

  • 在同一個事務中進行加鎖操做
  • 被加鎖的記錄在同一個頁面中
  • 加鎖的類型是同樣的
  • 等待的狀態是同樣的

鎖結構信息

而後咱們再來依此看下這個所結構每一個部分的信息都是什麼意思

  • 鎖所在的事務信息:不管是表級鎖仍是行級鎖,一個鎖屬於一個事務,這裏記載着該鎖對應的信息
  • 索引信息:對於行級鎖來講,須要記錄一下加鎖的記錄屬於哪一個索引
  • 表鎖/行鎖信息:行級鎖

    • Space_ID:記錄所在的表空間

    * Page Number:記錄所在的頁號

    • n_bits:一條記錄對應着一個比特;一個頁面包含多條記錄,用不一樣的比特來區分究竟是那一條記錄加了鎖,有個計算公式以下(公式中是取商)n_bits = (1+(n_recs+LOCK_PAGE_BITMAP_MARGIN)/ 8)x 8LOCK_PAGE_BITMAP_MARGIN是固定的值爲64,n_recs指當前界面一共有多少條記錄(包含僞記錄以及在垃圾鏈表中的記錄),
  • type_mode:32比特的數

    • lock_mode(鎖模式):低4比特位表示

      • LOCK_AUTO_INC(十進制的4):表示AUTO-INC鎖
      • LOCK_IS(十進制的0):表示共享意向鎖,IS鎖
      • LOCK_IX(十進制的1):表示獨佔意向鎖,IX鎖
      • LOCK_S(十進制的2):表示共享鎖,也就是S鎖
      • LOCK_X(十進制的3):表示獨佔鎖,也就是X鎖
    • lock_type(鎖類型):第5~8比特位表示

      • LOCK_TABLE(十進制的1):當第5比特位設置爲1時,表示表級鎖
      • LOCK_REC(十進制的32):當第6比特位設置爲1時,表示行級鎖
    • rec_lock_type(行鎖的具體類型):其他的比特位表示

      • LOCK_ORDINARY(十進制的0):表示next-key鎖
      • LOCK_GAP(十進制的512):當第10比特位是1時,表示gap鎖
      • LOCK_REC_NOT_GAP(十進制的1024):也就是當第11比特設置爲1時,表示Record Lock(記錄鎖)
      • LOCK_INSERT_INTENTION(十進制的2048):也就是當第12比特設置爲1時,表示Insert Intention Lock(插入意向鎖)
      • LOCK_WAIT(十進制的256):也就是當

        • 第9比特設置爲1時,表示is_waiting爲true,即當前事務獲取鎖失敗,處於等待狀態
        • 第9比特設置爲0時,表示is_waiting爲false,即當前事務獲取鎖成功
    • 其餘信息:此文章不討論
    • 一堆比特位:此文章不討論

    舉個例子

    事務T1 要給user表中的記錄加鎖,假設這些記錄存儲在表空間號爲20,頁號爲21的頁面上,T1給id=1的記錄加S型Record Lock鎖,假如當前頁面一共有5條記錄(3條用戶記錄和2條僞記錄)

    過程:先給表加IS鎖,不過咱們如今不關心,只關心行級鎖
    具體生成的所結構以下圖所示

    最後

    • 快過年啦,小杰可能也須要休息一下下,由於最近都周更(雖然上週有點事沒更,打臉),週末徹底沒有其他時間了
    • 感受和朋友家人們聯繫有點少了,過年回家鞏固下感情和朋友們聊聊天吹吹牛逼,順便維護下峽谷的治安
    • 最後祝關注java小杰要加油的寶貝兒們
    • 脫單暴富事事順,升職加薪牛哄哄!

    好文推薦

    相關文章
    相關標籤/搜索