read committed 比 serializable 快但弱一致性,适用于写入密集型业务;serializable 保证强一致但性能低,仅用于金融对账等场景,且需重试逻辑。

READ COMMITTED 比 SERIALIZABLE 快,但不是所有场景都适用
事务隔离级别本质是「一致性」和「并发性能」的权衡。READ COMMITTED 允许其他事务在当前事务执行期间修改并提交数据,因此不加范围锁、不阻塞读写,吞吐高;SERIALIZABLE 则通过加锁或快照序列化保证绝对一致,但容易触发锁等待甚至死锁,QPS 明显下降。
实操建议:
- Web API 写入密集型业务(如订单创建)默认用
READ COMMITTED,避免长事务拖慢整体响应 - 金融类对账、库存扣减等强一致性场景,才考虑
SERIALIZABLE,且必须配合重试逻辑 - postgresql 的
SERIALIZABLE是可串行化快照(SSI),比 mysql 的传统锁实现更轻量,但仍有约 5–10% 性能损耗 - MySQL 默认
REPEATABLE READ在某些 UPDATE 场景下会隐式升级为间隙锁,实际开销接近SERIALIZABLE,需用EXPLAIN看执行计划确认
脏读、幻读、不可重复读到底在哪一级别出现
这些现象不是“会不会发生”,而是“数据库是否保证不发生”。不同级别只承诺屏蔽其中一部分,其余靠应用层兜底。
常见错误现象:
-
READ UNCOMMITTED:能看到未提交事务的数据,select返回脏数据,生产环境禁用 -
READ COMMITTED:解决脏读,但同一事务中两次SELECT可能返回不同结果(不可重复读),INSERT后再SELECT可能多出新行(幻读) -
REPEATABLE READ(MySQL):解决脏读和不可重复读,但幻读仍存在(仅靠行锁无法拦截新插入) -
SERIALIZABLE:理论上杜绝全部三类,但代价是锁表或全扫描级锁,SELECT也可能被阻塞
PostgreSQL vs MySQL 隔离级别行为差异
名字一样,语义未必相同。尤其 REPEATABLE READ 在两者中完全不是一回事。
使用场景与参数差异:
- PostgreSQL 的
REPEATABLE READ实际等价于标准 SQL 的SERIALIZABLE(SSI 实现),不会出现幻读,但可能因冲突自动回滚,报错:could not serialize access due to concurrent update - MySQL 的
REPEATABLE READ基于 MVCC + 间隙锁,不自动回滚,但幻读真实存在——比如SELECT ... for UPDATE不锁新插入行,后续INSERT仍可成功 - 连接字符串里设
isolation_level=serializable在 SQLAlchemy 中对 PostgreSQL 有效,但在 MySQL 中只是降级为REPEATABLE READ,不会真正串行化 - 跨数据库迁移时,不能只改配置项名,必须重审业务逻辑是否依赖某一级别的具体行为
如何测出你真正在用哪个隔离级别
数据库客户端显示的级别,不等于当前事务生效的级别。很多 ORM 或连接池会在建连后执行 SET TRANSACTION ISOLATION LEVEL,但也可能被中间件覆盖或忽略。
实操建议:
- MySQL 中查当前会话级别:
SELECT @@transaction_isolation;查全局默认:SELECT @@global.transaction_isolation - PostgreSQL 中用:
SHOW transaction_isolation,注意它反映的是当前事务开始时的设置,不是连接初始化值 - 在事务内执行
SELECT pg_backend_pid(), now()并另起一个会话观察锁状态,比看配置更可靠 - ORM 如 django 的
atomic(using='xxx', savepoint=True)不改变隔离级别,只控制回滚点,别误以为它提升了隔离强度
真正影响性能的从来不是“选哪个级别”,而是你有没有意识到:同一张表上,SELECT 和 UPDATE 在不同级别下加的锁类型、范围、持续时间完全不同。这点最容易被忽略。