一次脏读的复现:事务隔离级别速览
发布时间:2025-12-27 16:24       
更新时间:2025-12-27 18:25
开两个客户端会话,几分钟能把脏读完整复现一遍。做完这个实验,四个事务隔离级别的差别就不用死记硬背了。整个实验只需要一张一行的表,成本几乎为零,值得每个后端都亲手跑一次。
准备:建表、调级别
mysql -uroot -p 登录后执行:
CREATE DATABASE isodb; USE isodb;
CREATE TABLE acct(id INT PRIMARY KEY, bal INT);
INSERT INTO acct VALUES(1, 100);
再开两个终端各连一个会话,下文称 A 和 B。实验在测试机上做,别拿生产库练手。两边都执行(MySQL 8.0 写法):
SET SESSION transaction_isolation = 'READ-UNCOMMITTED';
READ UNCOMMITTED(读未提交)是四个级别里最松的,允许读到别人还没提交的数据。四个级别从松到紧排,越紧并发越差,MySQL 默认取中间的 REPEATABLE READ,是安全和性能的折中。
A 会话:改数据,不提交
START TRANSACTION;
UPDATE acct SET bal = 200 WHERE id = 1;
执行完停在这里:返回结果会提示有一行已更新,修改在事务内已生效,只是对外还不可见。
B 会话:读到了未提交的 200
SELECT bal FROM acct WHERE id = 1; -- 返回 200
A 明明没提交,B 已经看到 200,这就是脏读(读到未提交数据)。此刻让 A 执行 ROLLBACK,B 手里的 200 就成了一个从未存在过的数字——如果 B 按它做业务决策,账就乱了。可以再开第三个会话(保持默认级别)查同一行,读到的仍是 100:同一份数据,不同隔离级别看到的是两个世界。
换 READ COMMITTED 再验证
两个会话执行 SET SESSION transaction_isolation = 'READ-COMMITTED';,重复上面的操作:B 查到的是 100,A 提交之后再查才变成 200,脏读消失。如果继续调到 REPEATABLE READ,还能看到另一个现象:A 提交后,B 在同一个事务里再查,读到的仍是 100,这就是可重复读的字面含义。四个级别用一句话各自概括:READ UNCOMMITTED 能读到未提交数据,基本不用;READ COMMITTED 只读已提交数据,但同一事务内两次读可能不同;REPEATABLE READ 保证事务内多次读一致,是 MySQL 的默认级别;SERIALIZABLE 完全串行,最安全也最慢。
收尾提醒:实验做完,把 A、B 两个会话的事务各自 COMMIT 或 ROLLBACK 干净再退出,避免遗留锁影响后续调试。