InnoDB的锁机制浅析(三)—幻读
- 作者: 五速梦信息网
- 时间: 2026年04月04日 13:39
文章总共分为五个部分:
前言
这一章节,我们通过幻读,逐步展开对InnoDB锁的探究。
1 幻读概念
解释了不同概念的锁的作用域,我们来看一下幻读到底是什么。幻读在RR条件下是不会出现的。因为RR是Repeatable Read,它是一种事务的隔离级别,直译过来也就是“在同一个事务中,同样的查询语句的读取是可重复”,也就是说他不会读到”幻影行”(其他事务已经提交的变更),它读到的只能是重复的(无论在第一次查询之后其他事务做了什么操作,第二次查询结果与第一次相同)。
for updateselect
当前读,又叫加锁读,或者 阻塞读。这种读取操作不再是读取快照,而是读取最新版本并且加锁。
快照读不会添加任何锁。
官方文档对于幻读的定义是这样的:
SELECT
The so-called phantom problem occurs within a transaction when the same query produces different sets of rows at different times.INSERTUPDATEDELETE
mysql的快照读,使得在RR的隔离级别上在next-Key的作用区间内,制造了一个快照副本,这个副本是隔离的,无论副本对应的区间里的数据被其他事务如何修改,在当前事务中,取到的数据永远是副本中的数据。
RR级别下之所以可以读到之前版本的数据,是由于数据库的MVCC(Multi-Version Concurrency Control,多版本并发控制)。参见InnoDB Multi-Versioning
有些文章中提到“RR也不能完全避免幻读”,实际上官方文档实际要表达的意义是“在同一个事务内,多次连续查询的结果是一样的,不会因其他事务的修改而导致不同的查询结果”,这里先给出实验结论:
1.当前事务如果未发生更新操作(增删改),快照版本会保持不变,多次查询读取的副本是同一个。
2.当前事务如果发生更新(增删改),再次查询时,会刷新快照版本。
示例的基础是一个只有两列的数据库表。
mysql> CREATE TABLE test (
id int(11) NOT NULL,
code int(11) NOT NULL,
PRIMARY KEY(id),
KEY (code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
mysql> INSERT INTO test(id,code) values(1,1),(10,10);
2 RC级别下的幻读
SET SESSION tx_isolation='READ-COMMITTED';
| 事务一 | 事务二 |
|---|---|
| mysql> SET SESSION tx_isolation='READ-COMMITTED'; mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> select * from test where code > 8; +----+------+ | id | code | +----+------+ | 10 | 10 | +----+------+ 1 row in set (0.01 sec) |
|
| mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> insert into test values(9,9); Query OK, 1 row affected (0.00 sec) mysql> commit; Query OK, 0 rows affected (0.00 sec) |
|
| mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> select * from test where code > 8; +----+------+ | id | code | +----+------+ | 9 | 9 | +----+------+ | 10 | 10 | +----+------+ 1 row in set (0.01 sec) |
RC(Read Commit)隔离级别可以避免脏读,事务内无法获取其他事务未提交的变更,但是由于能够读到已经提交的事务,因此会出现幻读和不重复读。
也就是说,RC的快照读是读取最新版本数据,而RR的快照读是读取被next-key锁作用区域的副本
3 RR级别下能否避免幻读?
我们先来模拟一下RR隔离级别下没有出现幻读的情况:
开启第一个事务并执行一次快照查询。
| 事务一 | 事务二 |
|---|---|
| mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> select * from test where code > 8; +----+------+ | id | code | +----+------+ | 10 | 10 | +----+------+ 1 row in set (0.01 sec) |
|
| mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> insert into test values(9,9); Query OK, 1 row affected (0.00 sec) mysql> commit; Query OK, 0 rows affected (0.00 sec) |
|
| mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> select * from test where code > 8; +----+------+ | id | code | +----+------+ | 10 | 10 | +----+------+ 1 row in set (0.01 sec) |
这两个事务的执行,有两个问题:
INSERTfor updateSELECT
2.数据库中的数据已经改变,为什么会读不到?
这个就是之前提到的next-key lock锁定的副本。RC及以下级别才会读到已经提交的事务。更多的业务逻辑是希望在某段时间内或者某个特定的逻辑区间中,前后查询到的数据是一致的,当前事务是和其他事务隔离的。这也是数据库在设计实现时遵循的ACID原则。
再给出RR条件下出现幻读的情形,这种情形不需要两个事务,一个事务就已经可以说明,
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from test where id>8;
+----+------+
| id | code |
+----+------+
| 10 | 10 |
+----+------+
1 row in set (0.00 sec)
mysql> update test set code=9 where id=10;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> select * from test where id>8;
+----+------+
| id | code |
+----+------+
| 10 | 9 |
+----+------+
1 row in set (0.00 sec)
至于RR隔离级别下到底会不会出现幻读,就需要看幻读的定义中的查询到底是连续的查询还是不连续的查询。如果认为RR级别下可能会出现幻读,那该级别下也会出现不重复读。
RR隔离级别下,虽然不会出现幻读,但是会因此产生其他的问题。
前提:当前数据表中只存在(1,1),(5,5),(10,10)三组数据。
SET SESSION tx_isolation='REPEATABLE-READ';
然后执行下列操作:
code>8
4 更新丢失(Lost Update)
4.1 更新丢失
除了上述这类问题外,RR还会有丢失更新的问题。
如下表给出的操作:
code>8code>8
SELECTUPDATEWHEREmatched: 0 Changed: 0
update test set code=100 where id=10;update test set code=200 where id=10;
Lost Update
4.2 乐观锁与悲观锁
这种情况下,引入我们常见的两种方式来解决该问题
UPDATEWHEREUPDATESELECTFOR UPDATEUPDATESELECT ... FOR UPDATESELECT ... LOCK IN SHARE MODE
当前读当前读
相关文章
-
InnoDB解决幻读的方案——LBCC&MVCC
InnoDB解决幻读的方案——LBCC&MVCC
- 互联网
- 2026年04月04日
-
InnoDB在MySQL默认隔离级别下解决幻读
InnoDB在MySQL默认隔离级别下解决幻读
- 互联网
- 2026年04月04日
-
Innodb之拷贝InnoDB表从一服务器到另一台服务器
Innodb之拷贝InnoDB表从一服务器到另一台服务器
- 互联网
- 2026年04月04日
-
InnoDB:Lock & Transaction
InnoDB:Lock & Transaction
- 互联网
- 2026年04月04日
-
Innodb 中 RR 隔离级别能否防止幻读?
Innodb 中 RR 隔离级别能否防止幻读?
- 互联网
- 2026年04月04日
-
innodb 是如何在可重下避免幻读复读的级别
innodb 是如何在可重下避免幻读复读的级别
- 互联网
- 2026年04月04日








