所謂的幻影問題發生在交易中,當相同的查詢在不同的時間產生不同的列集合時。例如,如果執行兩次 SELECT
,但第二次傳回第一次未傳回的列,則該列為「幻影」列。
假設在 child
表格的 id
欄位上有索引,並且您想要讀取並鎖定表格中所有識別碼值大於 100 的列,並打算稍後更新選定列中的某些欄位
SELECT * FROM child WHERE id > 100 FOR UPDATE;
查詢從 id
大於 100 的第一個記錄開始掃描索引。讓表格包含 id
值為 90 和 102 的列。如果掃描範圍內索引記錄上設定的鎖定沒有鎖定在間隙中進行的插入(在本例中,90 和 102 之間的間隙),則另一個會話可以將新的列插入表格,id
為 101。如果您在同一個交易內執行相同的 SELECT
,您將在查詢傳回的結果集中看到一個 id
為 101 的新列(「幻影」)。如果我們將一組列視為一個資料項目,則新的幻影子項將違反交易的隔離原則,即交易應該能夠運行,以便在其交易期間,其讀取的資料不會更改。
為了防止幻影,InnoDB
使用一種稱為下一個鍵鎖定的演算法,該演算法將索引列鎖定與間隙鎖定結合在一起。InnoDB
以這樣的方式執行列級鎖定:當它搜尋或掃描表格索引時,它會在它遇到的索引記錄上設定共用或獨佔鎖定。因此,列級鎖定實際上是索引記錄鎖定。此外,索引記錄上的下一個鍵鎖定也會影響索引記錄之前的「間隙」。也就是說,下一個鍵鎖定是索引記錄鎖定加上索引記錄之前間隙上的間隙鎖定。如果一個會話在索引中的記錄 R
上具有共用或獨佔鎖定,則另一個會話無法在索引順序中 R
之前的間隙中插入新的索引記錄。
當 InnoDB
掃描索引時,它也可以鎖定索引中最後一個記錄之後的間隙。這只是發生在前面的範例中:為了防止任何 id
大於 100 的插入到表格中,InnoDB
設定的鎖定包括對 id
值 102 之後的間隙的鎖定。
您可以使用下一個鍵鎖定在應用程式中實作唯一性檢查:如果您以共用模式讀取資料,並且沒有看到您要插入的列的重複項,那麼您可以安全地插入您的列,並且知道讀取期間您列的後繼者上設定的下一個鍵鎖定可防止任何人在此期間插入您列的重複項。因此,下一個鍵鎖定可讓您「鎖定」表格中某個東西的不存在。
間隙鎖定可以停用,如第 17.7.1 節「InnoDB 鎖定」中所述。這可能會導致幻影問題,因為當停用間隙鎖定時,其他會話可以將新的列插入間隙中。