mysql隔离级别的选择对事务的影响_mysql并发事务控制

来源:这里教程网 时间:2026-02-28 20:46:31 作者:

READ COMMITTED 为什么是 MySQL 默认且最常用的选择

MySQL InnoDB 默认隔离级别是

READ COMMITTED
,不是因为“最安全”,而是它在避免脏读的前提下,兼顾了并发性能与开发直觉。多数业务场景(如订单创建、库存扣减、用户资料更新)不需要可重复读语义,却极度敏感于锁等待和死锁。

READ COMMITTED
下,普通
SELECT
是快照读(不加锁),而
UPDATE
/
DELETE
只锁匹配到的行(基于聚簇索引),不会锁间隙
同一事务内多次执行相同
SELECT
,可能看到其他事务已提交的新数据 —— 这是预期行为,不是 bug
如果你在应用层做了“先查后更”的逻辑(比如查余额 → 判断是否足够 → 扣款),
READ COMMITTED
下仍可能因并发导致超扣,必须靠
SELECT ... FOR UPDATE
显式加锁

REPEATABLE READ 真正锁住的是什么,而不是“数据不变”

很多人误以为

REPEATABLE READ
能保证事务中所有
SELECT
结果一致,其实它只保证“同一查询在事务内返回相同快照”,但这个快照的生成时机是第一次 SELECT 执行时,且它的锁机制比表面更重。

UPDATE t SET x=1 WHERE id = 5
REPEATABLE READ
下不仅锁住
id=5
的行,还会锁住前后间隙(Next-Key Lock),防止幻读
这意味着一个看似简单的等值更新,可能意外阻塞其他事务对相邻 ID 的插入或更新 如果表没有显式主键或唯一索引,InnoDB 会用隐式聚簇索引,此时
REPEATABLE READ
的间隙锁范围更难预测,容易引发莫名锁等待

Serializable 会让事务退化成串行执行,但未必解决你想象的问题

SERIALIZABLE
是 SQL 标准定义的最高隔离级别,在 MySQL 中它会让所有普通
SELECT
隐式转为
SELECT ... LOCK IN SHARE MODE
,相当于给扫描到的每一行都加共享锁。

它确实能杜绝脏读、不可重复读、幻读,但代价是并发能力断崖式下降:两个事务哪怕只读不同行,只要扫描范围有重叠(比如都执行
SELECT * FROM t WHERE created_at > '2024-01-01'
),就可能互相阻塞
不推荐全局设置为
SERIALIZABLE
;若某段逻辑真需要强一致性,应单独用
SELECT ... FOR UPDATE
+ 明确 WHERE 条件,比升级整个事务级别更精准、开销更低
注意:某些 ORM(如 Django 的
select_for_update()
)在
REPEATABLE READ
下已能覆盖大部分强一致性需求,不必盲目升到
SERIALIZABLE

如何判断当前事务实际生效的隔离级别

MySQL 的隔离级别既可在会话级设置(

SET SESSION TRANSACTION ISOLATION LEVEL ...
),也可由客户端驱动自动协商(如 JDBC 的
connection.setTransactionIsolation()
),但最终以服务端实际生效为准。

查看当前会话级别:
SELECT @@transaction_isolation;
(MySQL 8.0+ 返回类似
READ-COMMITTED
查看全局默认:
SELECT @@global.transaction_isolation;
注意:有些云数据库(如阿里云 RDS)会屏蔽
@@global
的修改权限,且可能预设为
READ-COMMITTED
即使文档写的是 “兼容 MySQL 默认”
更关键的是,隔离级别只影响本事务内语句的行为;如果业务混用连接池、长事务、异步任务,同一个逻辑可能在不同连接上跑在不同级别下,务必在关键路径日志中打印
@@transaction_isolation

真正容易被忽略的,是隔离级别和索引策略的耦合关系 —— 没有合适索引时,

REPEATABLE READ
的 Next-Key Lock 会升级为全表锁,而
READ COMMITTED
也可能因无法精确定位行而锁更多记录。调优前先看
EXPLAIN
INFORMATION_SCHEMA.INNODB_TRX

相关推荐