MySQL資料庫InnoDB儲存引擎中的鎖機制--轉載

來源:互聯網
上載者:User

標籤:

原文地址:http://www.uml.org.cn/sjjm/201205302.asp

00 – 基本概念

當並發事務同時訪問一個資源的時候,有可能導致資料不一致。因此需要一種致機制來將訪問順序化。

鎖就是其中的一種機制。我們用商場的試衣間來做一個比喻。試衣間供許多消費者使用。因此可能有多個消費者同時要試衣服。為了避免衝突,試衣間的門上裝了鎖。試衣服的人在裡邊鎖住,其他人就不能從外邊開啟了。只有裡邊的人開門出來,外邊的人才能進去。

- 鎖的基本類型

資料庫上的操作可以歸納為兩中,讀和寫。多個事務同時讀一個對象的時候,是不會有衝突的。

同時讀和寫或者同時寫才會產生衝突。因此為了提高並發性,通常定義兩種鎖:

A. 共用鎖定(Shared Lock) 也叫讀鎖.

共用鎖定表示對資料進行讀操作。因此多個事務可以同時為一個對象加共用鎖定。

B. 獨佔鎖定(Exclusive Lock) 也叫寫鎖.

獨佔鎖定表示對資料進行寫操作。如果一個事務對對象加了獨佔鎖定,其他事務就不能再給它加任何鎖了。

- S、X鎖的相容性矩陣

對於鎖,通常會用一個矩陣來描述他們之間的衝突關係。

S X

S + –

X - -

+ 代表相容, -代表不相容

- 鎖的粒度

A. 表鎖(Table Lock)

對整個表加鎖,影響標準的所有記錄。通常用在DDL語句中,如DELETE TABLE,ALTER TABLE等。

B. 行鎖(Row Lock)

對一行記錄加鎖,隻影響一條記錄。通常用在DML語句中,如INSERT, UPDATE, DELETE等。

很明顯,表鎖影響整個表的資料,因此並發性不如行鎖好。

- 意圖鎖定(Intention Lock)

因為表鎖覆蓋了行鎖的資料,所以表鎖和行鎖也會產生衝突。如:

A. trx1 BEGI

B. trx1 給 T1 加X鎖,修改表結構。

C. trx2 BEGIN

D. trx2 給 T1 的一行記錄加S或X鎖(事務被阻塞,等待加鎖成功)。

trx1要操作整個表,鎖住了整個表。那麼trx2就不能再對T1的單條記錄加X或S鎖,去讀取或修這條記錄。

為了方便檢測表級鎖和行級鎖之間的衝突,就引入了意圖鎖定。

A. 意圖鎖定分為意向讀鎖(IS)和意向寫鎖(IX)。

B. 意圖鎖定是表級鎖,但是卻表示事務正在讀或寫某一行記錄,而不是整個表。

所以意圖鎖定之間不會產生衝突,真正的衝突在加行鎖時檢查。

C. 在給一行記錄加鎖前,首先要給該表加意圖鎖定。也就是要同時加表意圖鎖定和行鎖。

採用了意圖鎖定後,上面的例子就變成了:

A. trx1 BEGIN

B. trx1 給 T1 加X鎖,修改表結構。

C. trx2 BEGIN

D. trx2 給 T1 加IX鎖(事務被阻塞,等待加鎖成功)

E. trx2 給 T1 的一行記錄加S或X鎖.

- 表鎖的相容性矩陣

IS IX S X

IS + + + –

IX + + - -

S + - + -

X - - - -

+ 代表相容, -代表不相容

A. 意圖鎖定之間不會衝突, 因為意圖鎖定僅僅代表要對某行記錄進行操作。在加行鎖時,會判斷是否衝突。

01 – 行鎖

直觀的理解,行鎖就是要鎖住一行記錄,阻止其他事務操作該行記錄。這裡有一個隱含的邏輯:

A. 插入操作永遠不會被阻止,因為插入操作不會操作一條存在的記錄(這裡不考慮Insert duplicate的處理)。這個邏輯是對的嗎? 這和使用者的使用方式相關,有些情況下是使用者能接受的,有些情況下是使用者不能接受的。

- 幻讀(Phantom Read)

如果不阻止INSERT操作,就會產生幻讀.MySQL手冊中有幻讀的介紹.

A. MVCC 可以避免幻讀.但是MVCC只對SELECT語句有效,對於SELECT … [LOCK IN SHARE MODE | FOR UPDATE], UPDATE, DELETE語句無效。

B. 為了能夠通過鎖避免幻讀,採用了next-key的機制。next-key通過鎖住2個記錄之間的間隙,來阻止INSERT操作。

- 行鎖的模式

行鎖S、X鎖上做了一些精確的細分,在代碼中稱作Precise Mode。這些精確的模式,使的鎖的粒度更細小。可以減少衝突。

A. 間隙鎖(Gap Lock),只鎖間隙。

B. 記錄鎖(Record Lock) 只鎖記錄。

C. Next-Key Lock(代碼中稱為Ordinary Lock),同時鎖住記錄和間隙.

D. 插入意圖鎖(Insert Intention Lock),插入時使用的鎖。在代碼中,插入意圖鎖,

實際上是GAP鎖上加了一個LOCK_INSERT_INTENTION的標記.

MySQL手冊對這些模式有詳細的介紹.

- 行鎖模式的相容性矩陣

G I R N (已經存在的鎖,包括等待的鎖)

G + + + +

I - + + -

R + + - -

N + + - -

+ 代表相容, -代表不相容. I代表插入意圖鎖,

G代表Gap鎖,I代表插入意圖鎖,R代表記錄鎖,N代表Next-Key鎖.

S鎖和S鎖是完全相容的,因此在判別相容性時不需要對比精確模式。

精確模式的檢測,用在S、X和X、X之間。

這個矩陣是從lock0lock.c:lock_rec_has_to_wait()的代碼推出來的。從這個矩陣可以看到幾個特點:

A. INSERT操作之間不會有衝突。

B. GAP,Next-Key會阻止Insert。

C. GAP和Record,Next-Key不會衝突

D. Record和Record、Next-Key之間相互衝突。

E. 已有的Insert鎖不阻止任何準備加的鎖。

同時也有幾個疑問:

A. 為什麼插入意圖鎖不阻止間隙鎖?在特定的情況下會導致INSERT操作被無限期延遲。

B. 如果不阻止任何鎖,這個鎖還有必要存在嗎?

- 目前看到的作用是,通過加鎖的方式來喚醒等待線程。

- 但這並不意味著,被喚醒後可以直接做插入操作了。需要再次判斷是否有鎖衝突。

C. GAP+LOCK_INSERT_INTENTION標記的方式,能否直接變成INSERT_INTENTION鎖?

目前還在看。

- B+Tree 行鎖

InnoDB的行鎖並不是簡單的資料行鎖的概念。而是指每個B+Tree上的行鎖,也可以理解為每個Index上的行鎖。因此操作一行記錄時,有可能會加多個行鎖在不同的B+Tree上。如:

CREATE TABLE t1(c1 INT KEY, c2 int, c3 int, INDEX(c2));

INSERT INTO t1 VALUES(1, 1, 1), (3, 3, 3)

UPDATE t1 c3 = 10 WHERE c2 <= 2

UPDATE語句會同時在Secondary Index和Clustered Index上加鎖。

- 行鎖模式的使用

行鎖的這些模式都在什麼情況下使用呢? MySQL手冊有詳細的介紹。

A. Next-Key 使用在被WHERE條件用到的索引上(準確的說是用來做Search的索引上)。

上面的例子中,Index(c2)上使用 Next-Key Lock.

B. Record Lock使用在沒有被WHERE條件使用的索引上。上面的例子中,簇索引上使用Record Lock.因此上面的UPDATE語句會同時在加Index(c2)的鍵1上加Next-Key,在主鍵1上加record鎖。當另一個session並發插入(2,5,2),(3,5,2)時可以成功,但是(2,2,2)時會被阻塞。

Next-Key And Record

測試時發現,SELECT…[FOR UPDATE |LOCKIN SHARE MODE]可能會導致全部記錄被鎖住。

當表很小時,SELECT會採用全表掃描的方法。在使用這種方法時,遍曆了所有的資料,因此所有資料都被鎖住了。儘管對不合格記錄調用了ha_innobase::unlock_row(),但是在Repeatable Read層級時不會被釋放。也許該算一個Bug.

C. A、B同時適用於SELECT…[FOR UPDATE | LOCK IN SHARE MODE], UPDATE、DELETE語句。

D. GAP鎖顯然也是使用在WHERE條件使用的索引上。和Next-Key不同的是,GAP鎖只加在上邊界(第一個大於合格記錄)上。而Next-Key加在所有合格記錄上。上面例子中的條件c2=2的記錄,需要在c2=3上加一個GAP鎖。

? 正向查詢時,InnoDB中實際上在邊界上加的是Next-Key鎖。 這可能是受實現的限制。

目前使用GAP情況有:

– Supremum記錄上始終是一個GAP鎖

– 反向查詢(ORDER BY DESC)時.

– 等值匹配一個確切的索引值時,對下一條記錄加GAP鎖。

– 等值匹配一個確切的索引值的首碼時,對下一條記錄加GAP鎖。。

E. INSERT時,通常不加鎖。只有當其他事務在插入點加了Gap或Next-key鎖需要等待時,才會建立一個插入意圖鎖。這個鎖是在waiting狀態。

- 隔離等級對Next-Key鎖的影響

A. Read Uncommitted和Read Committed時,不需要在間隙上加鎖,Nexk-Key變成Record鎖。

B. Repeatable Reads 和 Serializable時,通常情況下使用Next-key鎖。

有2中情況不需要對間隙加鎖:

– 查詢一個唯一的值,如 WHERE c1 = 1, c1 是主鍵或唯一鍵,並且查詢結果中不含NULL欄位。

– 當innodb_locks_unsafe_for_binlog被開啟。這裡還是有一些值得思考的問題:

  • 從這個情況來看,UPDATE,DELETE時加間隙鎖完全是為了防止Master和Slave資料不一致。那麼不使用binlog時就沒有必要對DELETE, UPDATE加間隙鎖。
  • Row Format Binlog時,不加間隙鎖是否會引起Master, Slave不一至。
  • 即便設定了innodb_locks_unsafe_for_binlog,SELECT…[]是否可以不加間隙鎖。

判斷加什麼鎖的主要工作在row0sel.c:row_search_for_mysql()中。

02 – 延遲加鎖機制

如果一個表有很多的索引,那麼操作一個記錄時,豈不是要加很多鎖到不同的B-Tree上嗎?

先來看一個事務的狀態資訊:

CREATE TABLE t1(c1 INT KEY, c2 INT);

BEGIN;

INSERT INTO t1 VALUES(1, 1);

INSERT INTO t1 VALUES(2, 2);

SHOW ENGINE INNODB STATUS;

狀態資訊:

LIST OF TRANSACTIONS FOR EACH SESSION:

---TRANSACTION 501, ACTIVE 0 sec

1 lock struct(s), heap size 376, 0 row lock(s), undo log entries 2

– 隱式鎖

Lock 是一種悲觀的順序化機制。它假設很可能發生衝突,因此在操作資料時,就加鎖。

如果衝突的可能性很小,多數的鎖都是不必要的。

Innodb 實現了一個延遲加鎖的機制,來減少加鎖的數量,在代碼中稱為隱式鎖(Implicit Lock)。

隱式鎖中有個重要的元素,事務ID(trx_id).隱式鎖的邏輯過程如下:

A. InnoDB的每條記錄中都一個隱含的trx_id欄位,這個欄位存在於簇索引的B+Tree中。

B. 在操作一條記錄前,首先根據記錄中的trx_id檢查該事務是否是活動的事務(未提交或復原).

如果是活動的事務,首先將隱式鎖轉換為顯式鎖(就是為該事務添加一個鎖)。

C. 檢查是否有鎖衝突,如果有衝突,建立鎖,並設定為waiting狀態。如果沒有衝突不加鎖,跳到E。

D. 等待加鎖成功,被喚醒,或者逾時。

E. 寫資料,並將自己的trx_id寫入trx_id欄位。Page Lock可以保證操作的正確性。

相關代碼:

A. lock_rec_convert_impl_to_expl()將隱式鎖轉換成顯示鎖。

B. 加鎖和測試行鎖衝突都用lock_rec_lock(),它的第一個參數表示是否是隱式鎖。所以要特別注意這個參數。如果為TRUE,在沒有衝突時並不會加鎖。

C. 測試行鎖的衝突的具體內容在lock_rec_has_wait()

D. 建立waiting鎖是lock_rec_enqueue_waiting()

E. 建立行鎖是lock_rec_add_to_queue()

– 隱式鎖的特點

A. 只有在很可能發生衝突時才加鎖,減少了鎖的數量。

B. 隱式鎖是針對被修改的B+Tree記錄,因此都是Record類型的鎖。不可能是Gap或Next-Key類型。

– 隱式鎖的使用

A. INSERT操作只加隱式鎖,不需要顯示加鎖。

B. UPDATE,DELETE在查詢時,直接對查詢用的Index和主鍵使用顯示鎖,其他索引上使用隱式鎖。

理論上說,可以對主鍵使用隱式鎖的。提前使用顯示鎖應該是為了減少死結的可能性。

INSERT,UPDATE,DELETE對B+Tree們的操作都是從主鍵的B+Tree開始,因此對主鍵加鎖可以有效阻止死結。

– Secondary Index上的隱式鎖

前邊說了, trx_id只存在於主鍵上,那麼輔助索引上如何來實現隱式索引呢?

顯然是要通過輔助索引中的主索引值,在主鍵B+Tree上進行二次尋找。這個開銷是很大的。

InnoDB對這個過程有一個最佳化:

A. 每個頁上有一個MAX_TRX_ID,每次修改輔助索引的記錄時,都會更新這個最大事務ID。

B. 當判斷是否要將隱式鎖變為顯式鎖時,先將頁面的max_trx_id和事務列表的最小trx_id比較。如果max_trx_id比事務列表的最小trx_id還小,那麼就不需要轉換為顯示鎖了。

代碼在lock_sec_rec_some_has_impl_off_kernel()中

/* Some transaction may have an implicit x-lock on the record onlyif the max trx id for the page >= min trx id for the trx list, ordatabase recovery is running. We do not write the changes of a page max trx id to the log, and therefore during recovery, this value for a page may be incorrect. */

if (page_get_max_trx_id(page) < trx_list_get_min_trx_id()

&& !recv_recovery_is_on()) {

return(NULL);

}

03 – 鎖的實現

– 鎖的存放

A. table->locks 存放一個表的所有表級鎖。

B. lock_sys->rec_hash存放所有表的行鎖。Hash值根據(spaceid, pageno)來計算。

C. trx->trx_locks存放事務的所有鎖,包括表級鎖和行級鎖。一個事務的所有鎖,在事務結束時,一起釋放。代碼在lock_release_off_kernel().如果有等待的鎖可以被授權,則會將等待的鎖,轉變為被授權的鎖,並喚醒相應的事務。

– 行鎖的唯一識別

第一印象想到的是,用每行記錄的索引值來做行鎖的唯一識別.但是索引值佔用空間比較大。

InnoDB使用Page NO.+Heap NO.來做行鎖的唯一識別。我們可以將Heap no.理解為頁面上的一個自增數值。每條物理記錄在被建立時,都會分配一個唯一的heap no.

A. 索引值可以理解為一個邏輯值,page no. + heap no. 是物理的。

B. 物理的雖然佔用空間小,但是處理要複雜一些。如:在分裂一個B+Tree頁面時,一半的記錄要移到新的頁面中,因此要對存在的鎖進行遷移。

鎖移動的d函數有:lock_move_reorganize_page(), lock_move_rec_list_start(),

lock_move_rec_list_end().

在刪除和插入資料時,也要進行GAP鎖的繼承。lock_rec_inherit_to_gap()

lock_rec_inherit_to_gap_if_gap_lock().

– 死結(Deadlock)

A. 逾時機制。當要加的鎖和其他鎖衝突時,添加一個waiting鎖,並且返回DB_LOCK_WAIT錯誤。

row_mysql_handle_error調用srv_suspend_mysql_thread來掛起一個線程。

B. 死結檢測檢測機制。每當建立waiting鎖,都要調用lock_deadlock_occurs()進行死結的檢測。

死結檢測方法是Waits-For Graph.在lock_deadlock_recursive()中實現。

當發現死結後要選擇其中的一個事務,將其復原,來解除死結。選擇哪一個交易回復能?

– 如果一個事務修改了non-transactional表(如MyISAM表,修改不能復原),另一個表沒有。

則沒有修改non-transactional的會被復原。

– 如果2個事務都修改了non-transactional表或者都沒有。則比較2個事務修改的記錄數和加的鎖數量。總和小的事務會被復原。trx_weight_ge()實現這個邏輯。

MySQL資料庫InnoDB儲存引擎中的鎖機制--轉載

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.