1. 程式人生 > 實用技巧 >MySQL如何使用鎖解決幻讀

MySQL如何使用鎖解決幻讀

MySQLREPEATABLE READ級別解決了幻讀問題,解決方案有兩種,一種是MVCC版本控制鏈,具體可以參考這個,[MVCC 多版本控制鏈],還有就是通過加鎖的方式。這篇文章簡要介紹一下MVCC如何通過加鎖的方式來解決幻讀問題。

準備工作

還是一樣,先建立一張表,如下所示:

CREATE TABLE hero (
    number INT,
    name VARCHAR(100),
    country varchar(100),
    PRIMARY KEY (number)
) Engine=InnoDB CHARSET=utf8;

然後插入如下資料:

INSERT INTO hero VALUES
    (1, '劉備', '蜀'),
    (3, '諸葛亮', '蜀'),
    (8, '曹操', '魏'),
    (15, '荀彧', '魏'),
    (20, '孫權', '吳');

此時MySQL中的示意圖簡化如下所示:

前置知識

MySQL中行鎖分為兩種,共享鎖,S鎖,以及獨佔鎖,X鎖。當使用select * from ...或者是select * from ... in share mode這兩種語法時,會對進行搜尋的記錄上加上S鎖。當使用select * from ... for update這個語法時就會對記錄加X`鎖。

當一個事務獲取了一條記錄的S鎖,其他事務可以獲取該記錄的S鎖,但不可以獲取X鎖;當一個事務獲取了一條記錄的X鎖,其他事務不可以繼續獲取該條記錄的X鎖或者S鎖。

分析

Record Locks

當開啟一個事務,使用select * from語句時,InnoDB

會對搜尋的記錄進行加鎖,官方名稱為LOCK_REC_NOT_GAP,具體是S鎖還是X鎖,具體視select語句而定。例如現在查詢number值為8的記錄,

select * from hero where number=8;

此時示意圖如下所示:

Gap Locks

接下來就涉及到了MySQL如何利用加鎖來解決REPEATABLE READ級別解決幻讀了。上面說了,當使用select 語句查詢時,InnoDB會對記錄加記錄鎖,但這就出現一個問題了,這個事務執行第一次查詢時,另一個事務要插入的資料此時還不存在,這個事務就無法對這些幻影記錄加上記錄鎖啊,那該怎麼辦呢?為了解決這個問題,InnoDB

引入了一種新的鎖機制,稱之為LOCK_GAP間隙鎖。例如,我們可以在number值為8的那條記錄上新增一個gap鎖,如下所示:

當一條記錄被加上gap鎖時,就意味著不允許其他事務在number值8的前面的間隙,即(3,8)這個區間內插入新的記錄。比如說,現在有另一個事務想要插入一條記錄,(4,"張飛",“蜀”)。此時,定位到該條記錄的下一條記錄的number值為8,而這條記錄上又有一個gap鎖,因此這個插入操作就會被阻塞住,直到擁有這個gap鎖的事務提交之後,number列的值在(3,8)中的新記錄才能被插入。

gap鎖的主要作用就是為了防止插入幻影記錄,如果你對一條記錄加了gap鎖,並不會限制其他事務對這條記錄繼續加記錄鎖或者gap鎖。

Next-Key Locks

當我們既想鎖住某條記錄,又想阻止其他事務在該記錄前邊的間隙插入新紀錄,此時該怎麼辦呢?這個時候,InnoDB又出現了一個新的鎖結構,LOCK_ORDINARY,也被稱之為next-key鎖,臨鍵鎖。它其實是record鎖和gap鎖的結合體。如下所示,給number值為8的記錄加一個next-key鎖。

總結

  • InnoDB中存在著不同的鎖,當使用select語句查詢某條記錄時,InnoDB會對該記錄加記錄鎖,即record鎖;
  • 當一個事務對某條記錄加上gap鎖時,另一個事務此時向這條記錄前面間隙插入新記錄的操作將會被阻塞;
  • next-key鎖,又稱為臨鍵鎖,是記錄鎖和間隙鎖的結合體。既鎖住某條記錄,又會將該記錄前面的間隙一同鎖住,解決了MySQLREPEATABLE READ級別下的幻讀問題。