脏读、不可重复读、幻读分别对应事务隔离级别的三类问题:脏读在READ UNCOMMITTED下发生,READ COMMITTED及以上可避免;不可重复读在READ COMMITTED下出现,REPEATABLE READ通过MVCC解决;幻读在REPEATABLE READ下仍可能影响当前读,SERIALIZABLE才能完全避免。
脏读:读到了别人还没提交的修改
事务 A 修改了一行数据但没提交,事务 B 此时读到了这行“脏”数据。一旦 A 回滚,B 读到的就是根本不存在的状态。
隔离级别下必然发生;
及以上可避免
典型场景:报表系统误把临时草稿当真实数据统计
错误现象不是报错,而是业务逻辑出错——比如用户看到余额变多了,实际扣款却失败了
MySQL 默认是
,所以默认不会脏读,但显式设成
就会
不可重复读:同一事务里两次 SELECT 结果不一致
事务 A 第一次读某行,事务 B 提交了对这行的 UPDATE 或 DELETE,A 再读一次,发现数据变了——这不是幻觉,是真实更新被看到了。
级别下会发生;
(MySQL 默认)用 MVCC 快照避免了它
注意:这里改的是“已有行”,不是新增或删除行——那是幻读的范畴
示例:
执行两次,中间被别人
并提交,第二次结果就不同
性能影响:MVCC 在
下需维护多个版本,undo log 增长快,长事务会拖慢 purge 线程
幻读:查不到的记录突然“冒出来”
事务 A 按条件查了一批记录(比如
),事务 B 插入一条满足该条件的新记录并提交,A 再次执行相同查询,发现多了一条“幻影”记录。
MySQL(Linux)
MySQL 9.6.0是面向Linux平台的2026年创新版本,核心架构迎来重大革新。其将外键约束与级联操作从InnoDB引擎层上移至SQL层,确保所有数据变更均被完整记录至Binlog,彻底解决了CDC(变更数据捕获)与主从复制中的数据不一致难题。此外,该版本引入container_aware启动选项以原生适配容器环境,并对审计日志进行了组件化重构,为追求极致数据一致性与云原生体验的开发者提供了全新选择。
下载
MySQL 的
用间隙锁(gap lock)+ 行锁 + Next-Key Lock,在大多数情况下阻止幻读——但仅限于当前读(如
或
带条件)
纯快照读(普通
)在
下不会看到新插入的行,所以“感觉不到”幻读;但如果你做
,可能意外更新到 B 刚插入的那条,这就是幻读的实际影响
真正能完全避免幻读的只有
,但它会让并发写变成排队,吞吐暴跌
常见坑:开发以为加了事务就万事大吉,结果在
下做“先查后插”的业务逻辑(如防重单),没加
,导致两条一模一样的订单入库
怎么选隔离级别?别只看理论
MySQL 默认
是有代价的:间隙锁可能引发死锁,长事务让 undo log 膨胀,备份时
依赖它但也会被阻塞。
如果业务能接受“每次读都看到最新已提交数据”,且没有范围更新需求,
更轻量,还减少锁冲突
高并发写+范围条件更新(比如批量关单、改状态),务必确认是否用了
加锁,否则幻读隐患藏在逻辑深处
几乎不用在 OLTP 场景——它会让所有
都隐式加锁,连简单查询都可能互相等待
最常被忽略的一点:应用层重试逻辑和隔离级别要匹配。比如在
下重试 SELECT+UPDATE,可能比
下更安全;反过来,若假设“同一事务内数据不变”,却用了
,就会出错
READ UNCOMMITTEDREAD COMMITTEDREPEATABLE READREAD UNCOMMITTEDREAD COMMITTEDREPEATABLE READSELECT balance FROM accounts WHERE id = 1;UPDATE accounts SET balance = 100 WHERE id = 1;REPEATABLE READSELECT * FROM orders WHERE status = 'pending';REPEATABLE READSELECT ... FOR UPDATEUPDATESELECTREPEATABLE READUPDATE ... WHERE status = 'pending'SERIALIZABLEREPEATABLE READSELECT ... FOR UPDATEREPEATABLE READmysqldump --single-transactionREAD COMMITTEDSELECT ... FOR UPDATESERIALIZABLESELECTREAD COMMITTEDREPEATABLE READREAD COMMITTED