執行鎖讀 (LOCK IN SHARE MODE / FOR UPDATE) 時,InnoDB 對查詢過程中掃描的所有索引記錄加鎖。若掃描的是二級索引,該索引記錄指向的聚簇索引記錄、以及其他指向同一聚簇記錄的二級索引記錄全都會被加鎖。無索引可用時會掃描整個聚簇索引 → 鎖全表。
CREATE TABLE tb (id1 INT PRIMARY KEY, id2 INT UNIQUE, id3 INT, id4 INT, KEY idx(id3)) ENGINE=InnoDB; INSERT INTO tb VALUES (1,1,1,1), (5,5,5,5), (9,9,9,9);
SELECT * FROM tb WHERE id4=1 LOCK IN SHARE MODE (id4 無索引) → 掃描全表聚簇索引 (id1) 並加 S 鎖,所有二級索引 (id2, id3) 也被加鎖。SELECT * FROM tb WHERE id2=5 FOR UPDATE → 等待鎖超時。教訓:正確設計索引不僅關乎效能,更決定併發度。
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ; SELECT * FROM tb WHERE id3=5 FOR UPDATE; (id3 為普通索引)原因:RR 級別下,InnoDB 對二級索引採用 Next-Key Lock (Gap Lock + Record Lock),鎖定索引區間防止幻讀;同時向聚簇索引與同鍵值的其他二級索引傳播鎖。
LOCK IN SHARE MODE 加 S 鎖;FOR UPDATE 加 X 鎖。SELECT (非 Serializable) 為非阻塞快照讀,不加鎖。UPDATE/DELETE ... WHERE 加 X 鎖;INSERT 加 Record Lock。掌握此機制,才能寫出高併發、低衝突的 SQL。