InnoDB 使用一种称为 Next-Key Locking 的锁机制来解决幻读问题。幻读发生在一个事务在读取某个范围内的记录时,另一个事务在这个范围内插入新的记录。InnoDB 的 Next-Key Locking 结合了行锁(Row Lock)和间隙锁(Gap Lock),确保在执行范围查询时能够锁定现有记录以及它们之间的间隙,从而防止其他事务在该范围内插入新的记录。
Next-Key Locking 机制
Next-Key Locking 是一种在索引记录上应用的锁机制,它锁住的并不仅仅是索引记录本身,还包括索引记录之间的间隙。这种机制可以防止其他事务在锁定的范围内插入新的记录。
具体例子
假设你有一个包含以下记录的表 employees
:
sql
CREATE TABLE employees (
id INT PRIMARY KEY,
name VARCHAR(100)
);
INSERT INTO employees (id, name) VALUES (1, 'Alice'), (3, 'Bob'), (5, 'Charlie');
在使用可重复读(REPEATABLE READ)隔离级别时,InnoDB 如何避免幻读?考虑以下两个事务:
- 事务 A:执行一个范围查询。
- 事务 B:在事务 A 的范围内插入新的记录。
事务 A
sql
START TRANSACTION;
SELECT * FROM employees WHERE id BETWEEN 1 AND 5;
当事务 A 执行上述查询时,InnoDB 会对满足条件的所有记录(id=1
, id=3
, id=5
)以及这些记录之间的间隙((1,3)
, (3,5)
, (5,∞)
)加锁。
事务 B
sql
START TRANSACTION;
INSERT INTO employees (id, name) VALUES (2, 'David');
由于事务 A 已经锁定了 id
范围 [1,5]
及其间隙,所以事务 B 尝试在范围 [1,3]
的间隙中插入 id=2
的新记录时会被阻塞,直到事务 A 提交或回滚。
示例演示
创建表和插入数据
sql
CREATE TABLE employees (
id INT PRIMARY KEY,
name VARCHAR(100)
);
INSERT INTO employees (id, name) VALUES (1, 'Alice'), (3, 'Bob'), (5, 'Charlie');
事务 A
sql
START TRANSACTION;
SELECT * FROM employees WHERE id BETWEEN 1 AND 5; -- Next-Key Locking 在索引记录及其间隙上加锁
事务 B
sql
START TRANSACTION;
INSERT INTO employees (id, name) VALUES (2, 'David'); -- 此操作会被阻塞,直到事务 A 提交或回滚
提交事务 A
sql
COMMIT; -- 提交事务 A 后,事务 B 可以继续执行
总结
InnoDB 使用 Next-Key Locking 机制通过结合行锁和间隙锁来解决幻读问题。当一个事务在执行范围查询时,InnoDB 会锁定查询结果集中的每一条记录以及这些记录之间的间隙。这确保了在范围内的任何插入操作都被阻塞,从而避免了幻读。
这种锁机制在可重复读(REPEATABLE READ)隔离级别下有效地避免了幻读,使得事务在读取范围内的数据时,不会因为其他事务的插入而读取到新的"幻影"记录。