Mysql技術內幕InnoDB儲存引擎讀書筆記--《六》鎖

FreeeLinux發表於2017-07-01

資料庫系統區別於檔案系統的一個關鍵特性,一方面要最大程度地利用資料庫的併發訪問,另外一方面還要確保每個使用者能以一致的方式讀取和修改資料。

鎖的型別

InnoDB儲存引擎實現瞭如下兩種標準的行級鎖:

  • 共享鎖(S LOCK),允許事務讀一行資料
  • 排它鎖(X LOCK),允許事務刪除或者更新一行資料

當一個事務已經獲得了行r的共享鎖,那麼另外的事務可以立即獲得行r的共享鎖,因為讀取並沒有改變行r的資料,我們稱這種情況為鎖相容。但如果有事務想獲得行r的排它鎖,則它必須等待事務釋放行r上的共享鎖——這種情況我們成為鎖不相容。

InnoDB儲存引擎支援多粒度鎖定,這種鎖定允許在行級上的鎖和表級上的鎖同時存在。為了支援在不同粒度上進行加鎖操作,InnoDB儲存引擎支援一種額外的鎖方式,我們稱之為意向鎖。意向鎖是表級別的鎖,其設計目的主要是為了在一個事務中揭示下一行將被請求的鎖的型別。

  • 意向共享鎖(IS Lock),事務想要獲得一個表中某幾行的共享鎖。
  • 意向排它鎖(IX Lock),事務想要獲得一個表中某幾行的排它鎖。
    因為InnoDB支援的是行級別的鎖,所以意向鎖其實不會阻塞除全表掃以外的任何請求。

一致性的非鎖定讀操作

一致性的非鎖定讀操作(consistent nonlocking read)是指InnoDB儲存引擎通過行多版本控制(multi versioning)的方式來讀取當前執行時間資料庫中行的資料。如果讀取的行正在執行DELETE、UPDATE操作,這時讀取操作不會因此等待行上的鎖釋放,相反,儲存引擎會去讀取一個快照資料。

快照資料是指該行之前版本的資料,該實現是通過Undo段來實現。而Undo用來在事務中回滾資料,因而快照資料本身是沒有額外的開銷。此外,讀取快照資料是不必要上鎖的,因為沒有必要對歷史的資料進行修改。

這種併發控制,就是多版本併發控制。

在Read Comitted和Repeatable Read(InnoDB儲存引擎的預設事務隔離級別)下,InnoDB儲存引擎使用非鎖定的一致性讀。然後,對於快照資料的定義卻不相同。在Read Comitted事務隔離級別下,對於快照資料,非一致性讀總是讀取被鎖定行的最新一份快照資料。在Repeatable事務隔離級別下和Repeatable Read事務隔離級別下,對於快照資料,非一致性讀總是讀取事務開始時的行資料版本。

所以,對於Read Commited的事務隔離級別而言,其實違反了事務的隔離性。

加鎖方式

  • SELECT…FOR UPDATE 對讀取的行記錄加一個X鎖。其他事務想在這些行上加任何鎖都會被阻塞。
  • SELECT…LOCK IN SHARE MODE 對讀取的行記錄加一個S鎖。其他事務可以向鎖定的記錄加S鎖,但是對於家X鎖,則會被阻塞。

自增長和鎖

Mysql的auto_increment這個自增長計數器是加鎖的,使用如下語句來得到計數器的值:
SELECT MAX(auto_inc_col) FROM t FOR UPDATE;
這個鎖其實是採用一種特殊的表鎖機制,為了提高插入的效能,鎖不是再一個事務完成後才釋放,而是在完成對自增長值插入的SQL語句後立即釋放。

外來鍵和鎖

對於外來鍵值得插入或者更新,首先需要查詢父表中的記錄,即SELECT父表,但是對於父表的SELECT操作,不是使用一致性非鎖定讀的方式,因為這樣會發生資料不一致的問題,因此這時使用的是SELECT … LOCK IN SHARE MODE方式,主動對父表加一個S鎖,如果這時父表上已經這樣加X鎖,那麼子表上的操作會被阻塞。

鎖和演算法

InnoDB儲存引擎有3種行鎖的演算法設計:

  • Record Lock:單個行記錄上的範圍
  • Gap Lock:間隙鎖,鎖定一個範圍,但不包含記錄本省
  • Next-Key Lock: Gap Lock + Record Lock,鎖定一個範圍,並且鎖定記錄本身。

Record Lock總是會鎖住索引記錄,如果InnoDB儲存引擎建立的時候沒有設定任何一個索引,這時InnoDB儲存引擎會使用隱式的主鍵來進行鎖定。

比如:`SELECT * FROM t WHERE a < 6 lock in share mode,該語句會鎖定(-oo, 6)這個數值區間的所有數值。

丟失更新

即多使用者計算機系統下有可能產生的問題,使用者同一個賬號分別在不同機器上登入轉賬,如果不加鎖,可能轉賬金額錯誤,必須通過加鎖改為序列操作。

髒讀

即一個事務可以讀到另一個事務中未提交的資料,違反了資料庫的隔離性。發生條件:READ UNCOMMITED,這個隔離級別在Mysql中不使用。

不可重複讀

不可重複讀是指在一個事務內多次讀同一資料。在這個事務還沒有結束時,另外一個事務也訪問該資料。有可能出現第一個事務兩次讀的資料不一樣,即第二次讀的資料受到另外一個事務的影響。

不可重複讀和髒讀的區別是:髒讀是讀到未提交的資料;而不可重複讀讀到的確實是已經提交的資料,但是其違反了資料庫事務一致性的要求

InnoDB的預設事務隔離級別是READ REPEATABLE,採用Next-Key Lock演算法,解決了不可重複讀(幻讀)問題。在Next-Lock Key 演算法下,不僅僅是鎖住掃描到的索引,而且還鎖住這些索引覆蓋的範圍(gap)。因此對於這個範圍內的插入都是不允許的。

死鎖

InnoDB儲存引擎並不會回滾大部分的錯誤異常,但是死鎖除外。發生思索後,InnoDB儲存引擎會馬上回滾一個事務。(另一個事務就可以獲取資源了)

鎖升級

鎖升級(Lock Escalation)是指將當前鎖的粒度降低。比如1000個行鎖升級為一個頁鎖,或者將頁鎖升級為表鎖。

  • 由一句單獨的SQL語句在一個物件上持有的鎖的數量超過了閾值,預設的這個閾值為5000。值得注意的是,如果是不同物件的話,則不會發生鎖升級。
  • 鎖資源佔用的記憶體超過了啟用記憶體的40%時,就會發生鎖升級。

相關文章