在 MySQL 5.5.20 上通過雙 Session 並發實驗,驗證 InnoDB 在不同表結構(無主鍵、有主鍵、無索引、有索引)下的鎖行為,結論:命中索引(含主鍵)才是行鎖,否則退化為表鎖。
CREATE TABLE test (a INT(5), b VARCHAR(10), c VARCHAR(10));
INSERT INTO test VALUES (1,'111','111'),(2,'222','222'),(3,'333','333'),
(4,'444','444'),(5,'555','555'),(6,'666','666');
SET AUTOCOMMIT=0;
| 場景 | Session 1 | Session 2 | 結果 |
|---|---|---|---|
| 無主鍵,更新不同行 (a=1 vs a=2) | UPDATE ... WHERE a=1 | UPDATE ... WHERE a=2 | 表鎖:S2 等待超時 |
| 無主鍵,INSERT + 更新不同行 | INSERT (7,'777','777') | UPDATE ... WHERE a=2 | 表鎖:S2 等待超時 |
| 加主鍵 (PK=a),更新不同行 | UPDATE ... WHERE a=1 | UPDATE ... WHERE a=2 | 行鎖:並發成功 |
| 有主鍵,更新同一行 | UPDATE ... WHERE a=1 | UPDATE ... WHERE a=1 | 行鎖:S2 等待(正常行鎖競爭) |
| 有主鍵,INSERT + 更新不同行 | INSERT (8,'888','888') | UPDATE ... WHERE a=1 | 行鎖:並發成功 |
| 無索引,更新非索引列不同值 (b='111' vs '222') | UPDATE ... WHERE b='111' | UPDATE ... WHERE b='222' | 表鎖:S2 等待超時 |
| 建索引 (IND=b),更新不同值 | UPDATE ... WHERE b='111' | UPDATE ... WHERE b='222' | 行鎖:並發成功 |
EXPLAIN 確認 type=ref/eq_ref 而非 ALL。