repeatable read 仅保证事务内快照读结果一致,不提供绝对数据一致性;它解决不可重复读但无法杜绝幻读和业务错乱,需依赖约束、原子性及应用层逻辑协同保障。

MySQL 默认的 REPEATABLE READ 隔离级别能保证单事务内多次读取结果一致,但无法完全避免幻读;一致性不是隔离级别直接提供的结果,而是靠原子性、约束校验、应用层逻辑共同维护的。
为什么「可重复读」不等于「数据绝对一致」
很多人误以为设成 REPEATABLE READ 就万事大吉,其实它只解决「不可重复读」,不天然杜绝业务层面的数据错乱。比如两个事务同时执行「查余额→扣款→更新」,即使都用 REPEATABLE READ,仍可能因未加锁导致超扣——因为 MVCC 快照不阻止其他事务插入或修改,只是让你看不到那些变更。
- 快照读(
SELECT)不加锁,依赖事务启动时的 Read View,所以读不到并发修改 - 当前读(
SELECT ... FOR UPDATE、UPDATE、DELETE)会加行锁或间隙锁,才真正阻塞冲突操作 -
REPEATABLE READ下的幻读虽被间隙锁抑制,但仅限于显式当前读;普通SELECT仍可能在后续范围查询中“看到”新插入的行(若该插入发生在你事务启动之后、且未被间隙锁覆盖)
READ COMMITTED 和 REPEATABLE READ 的关键差异在哪
差别不在“能不能读到已提交数据”,而在于 Read View 的生成时机和锁行为:
-
READ COMMITTED:每次SELECT都生成新 Read View,所以两次查询之间可能看到其他事务的提交结果 → 出现不可重复读 -
REPEATABLE READ:事务启动时生成唯一 Read View,后续所有快照读都复用它 → 同一事务内结果稳定 - 两者对
UPDATE加锁方式相同(都是当前读,走行锁),但REPEATABLE READ在范围条件上会额外加间隙锁,READ COMMITTED不加 → 这是幻读控制差异的根源 - InnoDB 在
READ COMMITTED下关闭间隙锁,所以即便有唯一索引,INSERT也可能成功插入“本该被锁住”的间隙
一致性到底靠什么保障,而不是靠隔离级别
隔离级别只管「并发时看到什么」,不管「数据本身合不合理」。真正的一致性需要多层协同:
- 数据库约束(
PRIMARY KEY、FOREIGN KEY、CHECK)在写入时强制校验,这是最基础的一致性防线 - 事务原子性(
ROLLBACK/COMMIT)确保中间状态不对外可见,但前提是 SQL 逻辑本身没漏写回滚分支 - 应用层必须自己处理「检查-执行」类逻辑,比如余额不足时拒绝扣款——不能指望隔离级别替你做业务判断
- 外键约束在
REPEATABLE READ下可能因延迟加载触发不一致行为(如级联删除未及时生效),需配合FOR UPDATE显式加锁
最容易被忽略的是:MVCC 快照只对普通 SELECT 生效;一旦用了 SELECT ... LOCK IN SHARE MODE 或 FOR UPDATE,就立刻退化为当前读,快照失效,锁机制接管。别以为开了 REPEATABLE READ 就可以省掉所有锁。











