1. 程式人生 > 實用技巧 >【mysql】- 鎖篇(上)

【mysql】- 鎖篇(上)


回顧

問題

  • 事務併發執行時可能帶來各種問題,併發事務訪問相同記錄的情況大致可以劃分為3種
    • 讀-讀情況:即併發事務相繼讀取相同的記錄
      • 讀取操作本身不會對記錄有什麼影響,並不會引起什麼問題,所以允許這種情況的發
    • 寫-寫情況:即併發事務相繼對相同的記錄做出改動
      • 任何一種隔離級別都不允許這種問題的發生。所以在多個未提交事務相繼對一條記錄做改動時,需要讓它們排隊執行,這個排隊的過程其實是通過來實現的。這個所謂的其實是一個記憶體中的結構,在事務執行前本來是沒有鎖的,也就是說一開始是沒有鎖結構和記錄進行關聯的
    • 讀-寫寫-讀情況:也就是一個事務進行讀取操作,另一個進行改動操作
      • 這種情況下可能發生髒讀
        不可重複讀幻讀的問題

解決上述問題方案

  • 讀操作利多版本併發控制( MVCC ),寫操作進行加鎖
  • 讀、寫操作都採用加鎖的方

一致性讀(Consistent Reads)

  • 事務利用MVCC進行的讀取操作稱之為一致性讀,或者一致性性鎖讀,有的地也稱之為快照讀。所有普通的SELECT語句( plain SELECT )在READ COMMITTEDREPEATABLE READ隔離級別下都算是一致性讀一致性讀並不會對錶中的任何記錄做加鎖操作,其他事務可以自由的對錶中的記錄做改動

鎖定讀(Locking Reads)

併發事務的讀-讀情況並不會引起什麼問題,不過對於寫-寫讀-寫

寫-讀這些情況可能會引起一些問題,需要使用MVCC或者加鎖的方式來解決它們。在使用加鎖的方式解決問題時,由於既要允許讀-讀情況不受影響,又要使寫-寫讀-寫寫-讀情況中的操作相互阻塞

  • 共享鎖和獨佔鎖

    • 共享鎖 ,英文名:Shared Locks,簡稱S鎖。在事務要讀取一條記錄時,需要先獲取該記錄的S鎖
    • 獨佔鎖 ,也常稱排他鎖,英文名:Exclusive Locks,簡稱X鎖。在事務要改動一條記錄時,需要先獲取該記錄的X鎖
    • 上述兩種鎖的場景
      • 假如事務T1首先獲取了一條記錄的S鎖之後,事務T2接著也要訪問這條記錄:
        • 如果事務T2想要再獲取一個記錄的S鎖,那麼事務T2也會獲得該鎖,也就意味著事務T1
          T2在該記錄上同時持有S鎖
        • 如果事務T2想要再獲取一個記錄的X鎖,那麼此操作會被阻塞,直到事務T1提交之後將S鎖釋放掉
      • 如果事務T1首先獲取了一條記錄的X鎖之後,那麼不管事務T2接著想獲取該記錄的S鎖還是X鎖都會被阻塞,直到事務T1提交
      • 故我們說S鎖S鎖是相容的,S鎖X鎖是不相容的,X鎖X鎖也是不相容的
  • 鎖定讀的語句

    • 在採用加鎖方式解決髒讀不可重複讀幻讀這些問題時,讀取一條記錄時需要獲取該下該記錄的S鎖,其實這是不嚴謹的,有時候想在讀取記錄時就獲取記錄的X鎖,來禁用別的事務讀寫該記錄
      • 對讀取的記錄加S鎖
        • 也就是在普通的SELECT語句後邊加,如果當前事務執行了該語句,那麼它會為讀取到的記錄加S鎖,這樣允許別的事務繼續獲取這些記錄的S鎖(比方說別的事務也使用語句來讀取這些記錄),但是不能獲取這些記錄的X鎖(比方說使SELECT ... FOR UPDATE語句來讀取這些記錄,或者直接修改這些記錄)。如果別的事務想要獲取這些記錄的X鎖,那麼它們會阻塞,直到當前事務提交之後將這些記錄上的S鎖釋放掉
      • 對讀取的記錄加X鎖
        • SELECT ... FOR UPDATE;也就是在普通的SELECT語句後邊加FOR UPDATE,如果當前事務執行了該語句,那麼它會為讀取到的記錄加X鎖,這樣既不允許別的事務獲取這些記錄的S鎖(比方說別的事務使用語句來讀取這些記錄),也不允許獲取這些記錄的X鎖(比方說使用SELECT ... FOR UPDATE語句來讀取這些記錄,或者直接修改這些記錄)。如果別的事務想要獲取這些記錄的S鎖或者X鎖,那麼它們會阻塞,直到當前事務提交之後將這些記錄上的X鎖釋放掉
  • 寫操作

    • 平常所用到的寫操作DELETEUPDATEINSERT這三種:
      • DELETE:對一條記錄做DELETE操作的過程其實是先在B+樹中定位到這條記錄的位置,然後獲取一下這條記錄的X鎖,然後再執行delete mark操作。我們也可以把這個定位待刪除記錄在B+樹中位置的過程看成是一個獲取X鎖的鎖定讀
      • UPDATE:
        • 在對一條記錄做UPDATE操作時分為三種情況:
          • 如果未修改該記錄的鍵值並且被更新的列佔用的儲存空間在修改前後未發生變化,則先在B+樹中定位到這條記錄的位置,然後再獲取一下記錄的X鎖,最後在原記錄的位置進行修改操作。其實我們也可以把這個定位待修改記錄在B+樹中位置的過程看成是一個獲取X鎖鎖定讀
          • 如果未修改該記錄的鍵值並且至少有一個被更新的列佔用的儲存空間在修改前後發生變化,則先在B+樹中定位到這條記錄的位置,然後獲取一下記錄的X鎖,將該記錄徹底刪除掉(就是把記錄徹底移入垃圾連結串列),最後再插入一條新記錄。這個定位待修改記錄在B+樹中位置的過程看成是一個獲取X鎖鎖定讀,新插入的記錄由INSERT操作提供的隱式鎖進行保護
          • 如果修改了該記錄的鍵值,則相當於在原記錄上做DELETE操作之後再來一次INSERT操作,加鎖操作就需要按照DELETEINSERT的規則進行了
      • NSERT:一般情況下,新插入一條記錄的操作並不加鎖,通過一種稱之為隱式鎖來保護這條新插入的記錄在本事務提交前不被別的事務訪問

前邊提到的都是針對記錄的,也可以被稱之為行級鎖或者行鎖,對一條記錄加鎖影響的也只是這條記錄而已,我們就說這個鎖的粒度比較細;其實一個事務也可以在表級別進行加鎖,自然就被稱之為表級鎖或者表鎖,對一個表加鎖影響整個表中的記錄,我們就說這個鎖的粒度比較粗。給表加的鎖也可以分為共享鎖S鎖)和獨佔鎖X鎖

  • 多粒度鎖
    • 給表加S鎖
      • 如果一個事務給表加了S鎖,那麼:
        • 別的事務可以繼續獲得該表的S鎖
        • 別的事務可以繼續獲得該表中的某些記錄的S鎖
        • 別的事務不可以繼續獲得該表的X鎖
        • 別的事務不可以繼續獲得該表中的某些記錄的X鎖
    • 給表加X鎖
      • 如果一個事務給表加了X鎖(意味著該事務要獨佔這個表),那麼:
        • 別的事務不可以繼續獲得該表的S鎖
        • 別的事務不可以繼續獲得該表中的某些記錄的S鎖
        • 別的事務不可以繼續獲得該表的X鎖
        • 別的事務不可以繼續獲得該表中的某些記錄的X鎖