mysql如何分析脏读现象_mysql读未提交隔离级别测试

1次阅读

脏读仅在read uncommitted隔离级别下发生:两会话均执行set session transaction isolation level read uncommitted;会话a begin后update不commit,会话b select即可见未提交值,a rollback后该值消失。

mysql如何分析脏读现象_mysql读未提交隔离级别测试

怎么复现脏读:开启两个 mysql 会话,用 READ UNCOMMITTED

脏读只在事务隔离级别为 READ UNCOMMITTED 时可能真实发生。InnoDB 默认是 REPEATABLE READ,所以必须显式改掉——不是改配置文件,而是对当前会话执行:

SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

否则哪怕你写了 BEGIN,也看不到脏读。

常见错误现象:SELECT 看到另一事务还没 COMMIT 的修改,但对方随后 ROLLBACK 了,你的查询结果就“凭空多出/少掉”数据。

  • 两个会话都要先 SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
  • 会话 A 执行 BEGINUPDATE t SET val = 100 WHERE id = 1(不 COMMIT
  • 会话 B 此时执行 SELECT * FROM t WHERE id = 1,就能查到 val = 100
  • 会话 A 接着 ROLLBACK,会话 B 再查一次,值又变回原样

READ UNCOMMITTEDSELECT 不加锁,但要注意 MVCC 依然生效

很多人以为 READ UNCOMMITTED 就等于“裸读”,其实不是。InnoDB 仍会走 MVCC 路径,只是跳过了对“未提交版本”的可见性过滤——它会把其他事务未提交的最新行版本也纳入快照范围。

这意味着:SELECT 不会阻塞正在写同一行的事务(没 SELECT ... LOCK IN SHARE MODE 那种锁),但你也无法控制看到哪个“未提交版本”。如果多个事务并发改同一行,你查到的可能是中间某次修改,而它最终不会落地。

  • 不加锁 ≠ 不依赖 undo log;MVCC 结构仍在,只是可见性判断逻辑放宽
  • 无法预测看到的是谁的未提交变更,尤其在高并发 UPDATE 场景下,结果不稳定
  • INSERT ... SELECT 或子查询中出现脏读,比单条 SELECT 更难排查

为什么生产环境几乎不用 READ UNCOMMITTED

不是因为它“慢”,而是它破坏了应用层对数据一致性的基本假设。比如一个订单状态更新流程:A 服务标记“支付中”,B 服务读到后触发发券,结果 A 因异常回滚,“已发券”就成悬空状态。

性能上它确实略快(省了行可见性检查),但代价远超收益:

  • ORM 框架(如 mybatishibernate)通常默认走 REPEATABLE READ,硬切隔离级别容易和框架缓存、二级缓存冲突
  • MySQL 8.0+ 对 READ UNCOMMITTED 的优化极少,很多内部路径仍按标准 MVCC 处理
  • 监控和慢查日志里看不出脏读痕迹,问题往往暴露在业务对账或用户投诉时

测试时怎么确认真发生了脏读,而不是幻读或不可重复读

关键看三点:是否读到了**未提交**的数据、该数据之后被**回滚**、且读操作发生在**回滚之前**。幻读和不可重复读都要求对方已 COMMIT

最稳妥的验证方式:在会话 B 的 SELECT 后,立刻查 information_schema.INNODB_TRX,找那个未提交事务的 TRX_IDTRX_STATE = 'RUNNING',再等会话 A ROLLBACK 后对比数据变化。

  • 别只靠“两次 SELECT 结果不同”下结论——那可能是不可重复读(对方已 COMMIT
  • SHOW ENGINE INNODB STATUS 里的 TRANSACTIONS 部分能看见未提交事务,但刷新不及时
  • SELECT * FROM performance_schema.data_locks 看不到 READ UNCOMMITTED 的锁信息,因为它确实不加锁

实际测脏读,最难的不是让现象出现,而是确认你看到的“脏”确实是未提交的、且后续被丢弃的——这需要交叉验证事务状态,而不是只盯 SQL 结果。

text=ZqhQzanResources