read uncommitted 允许脏读,仅适用于实时监控等对一致性无要求的场景;常见错误是返回已回滚数据或导致业务误判;mysql不支持该级别,postgresql根本不实现。

Read Uncommitted:脏读都敢用,什么业务真需要?
它只在极少数场景下有意义——比如实时监控类系统,对数据一致性完全不敏感,只要“快”。例如传感器上报的原始日志流做实时聚合,允许把未提交的中间状态(比如某次回滚前的临时计数)算进去。
常见错误现象:SELECT 返回了被 ROLLBACK 掉的数据;应用逻辑因脏读产生误判(如重复发优惠券)。
- 别在金融、订单、库存等任何涉及状态变更的业务里用
- MySQL 默认不支持该级别(会自动降级为
READ COMMITTED),PostgreSQL 则根本不实现 - 即使启用,
SELECT仍可能被锁住(取决于存储引擎和查询条件),不是绝对“无锁”
Read Committed:大多数 Web 应用的默认安全线
这是 PostgreSQL、Oracle、SQL Server 的默认隔离级别,也是 MySQL 在 REPEATABLE READ 之外最常被显式指定的选项。它能挡住脏读,但不防不可重复读——两次 SELECT 同一条记录,中间被别人改过并提交,第二次就会看到新值。
使用场景:用户资料页查看、后台报表导出、API 查询接口——只要不要求“整个事务内视图完全一致”,它就足够稳。
- PostgreSQL 实现基于 MVCC,每次
SELECT看的是语句开始时刻的快照 - MySQL InnoDB 下,
SELECT不加锁,但UPDATE ... WHERE会先SELECT再锁行,可能触发 gap lock - 高并发更新同一行时,容易因锁等待超时抛出
Lock wait timeout exceeded
Repeatable Read:MySQL 默认,但别以为它真“可重复”
MySQL InnoDB 的 REPEATABLE READ 能保证事务中多次 SELECT 同一范围结果一致(靠间隙锁防幻读),但它不阻止其他事务插入新行再提交——所以严格来说,它只是“防止当前已存在行被修改/删除影响重复读”,不是 SQL 标准定义的完整 REPEATABLE READ。
容易踩的坑:
- 你在一个事务里查了
SELECT * FROM orders WHERE status = 'pending',得到 5 条;别人插入第 6 条并提交;你再查还是 5 条(InnoDB 锁住了这个范围) - 但如果你用
SELECT ... FOR UPDATE,InnoDB 会升级为 next-key lock,可能锁住更大范围,拖慢并发 - 跨库或读从库时,这个“一致性”立刻失效——主从延迟会让从库返回旧数据,跟事务隔离无关
Serializable:性能杀手,只在必须串行的环节开一刀
它让所有 SELECT 隐式变成 SELECT ... LOCK IN SHARE MODE(MySQL)或加 S 锁(PostgreSQL),写操作则加 X 锁,彻底消除幻读、不可重复读、脏读。代价是并发能力断崖下跌。
真实可用场景极少,典型例子:银行核心系统的日终批处理,需要确保某账户余额在整段核算逻辑中绝对不变;或者抢购类系统里,超卖检查 + 扣减必须原子执行,且不允许任何并发干扰。
- MySQL 下开启后,
INSERT可能因间隙冲突直接报Deadlock found when trying to get lock - PostgreSQL 使用 predicate lock,锁粒度更细,但仍有明显吞吐下降(实测 QPS 常掉 40%+)
- 别试图全局设成
SERIALIZABLE,它解决不了分布式事务问题,反而让单机瓶颈更突出
真正难的不是选哪个级别,而是意识到:同一个数据库里,不同业务表、甚至同一条 SQL 在不同上下文中的隔离需求都可能不同。比如订单主表用 REPEATABLE READ,而库存扣减那段逻辑单独包进 SERIALIZABLE 子事务——这种混用,比死守一个级别更常见,也更容易被忽略。










