當同一個查詢在不同的時間産生不同的行集時,就會出現所謂的幻像問題。例如,如果執行了兩次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使用了一種名為next-key鎖定的算法,它結合了索引行鎖和間隙鎖。InnoDB執行行級鎖的方式是這樣的:當它搜尋或掃描一個表索引時,它會在遇到的索引記錄上設定共享鎖或排他鎖。是以,行級鎖實際上是索引記錄鎖。此外,索引記錄上的next-key鎖也會影響該索引記錄之前的“間隙”。也就是說,next-key鎖是索引記錄鎖加上索引記錄之前的間隙鎖。如果一個會話在一個索引中的記錄R上有一個共享鎖或排他鎖,另一個會話不能在緊接在索引順序中的R之前的間隙中插入新的索引記錄。
當InnoDB掃描一個索引時,它也可以鎖定索引中最後一條記錄之後的間隙。就像在前面的例子中發生的那樣:為了防止插入id大于100的表,InnoDB設定的鎖包括id值102後面的間隙鎖。