mysql默认隔离级别为repeatable read,需显式设置read committed以避免并发读异常;可通过select @@transaction_isolation等命令查看级别,临时用set session,永久修改my.cnf并重启服务,orm需框架级配置生效。

MySQL 默认是 REPEATABLE READ,想切到 READ COMMITTED 必须显式设置,否则并发读场景下仍可能出现不可重复读以外的“意外一致”——这不是 bug,是 InnoDB 的行为设计使然。
查看当前会话和全局隔离级别
先确认你看到的到底是什么级别,避免误判。MySQL 8.0+ 已弃用 tx_isolation,统一用 transaction_isolation:
-
SELECT @@transaction_isolation;—— 查当前会话级别 -
SELECT @@global.transaction_isolation;—— 查全局默认值
如果返回是 REPEATABLE-READ(注意带短横线),说明还没改;返回 READ-COMMITTED 才算生效。
临时设置会话级为 READ COMMITTED
只影响当前连接,适合调试、测试或 ORM 显式控制事务时使用:
- 执行
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; - 必须在
START TRANSACTION或BEGIN之前设置,否则无效 - 该设置不会影响已开启但未提交的事务,只对后续新事务生效
- 连接断开后失效,不持久
例如:
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; START TRANSACTION; SELECT * FROM account WHERE id = 1; -- 这次读到的是其他事务已提交的最新值
永久设置为 READ COMMITTED(推荐生产环境)
修改配置文件才能确保所有新连接都按需启动,避免漏设:
- 编辑
my.cnf(Linux)或my.ini(Windows),在[mysqld]段落下添加:transaction-isolation = READ-COMMITTED - 值必须用大写+短横线格式,
READ COMMITTED或read committed都会报错启动失败 - 修改后必须重启 MySQL 服务:
systemctl restart mysql或service mysqld restart - 重启后检查
SELECT @@global.transaction_isolation;是否已更新
注意:已有连接不受影响,只有新建立的连接才会继承这个全局默认值。
ORM 或应用层调用时的常见陷阱
很多框架(如 MyBatis、Spring JDBC)会封装事务开启逻辑,直接在 SQL 里 SET 可能被覆盖:
- MyBatis 中用
SqlSession.openSession(TransactionIsolationLevel.READ_COMMITTED)才真正生效,仅在 XML 里写SET SESSION...无效 - Spring
@Transactional(isolation = Isolation.READ_COMMITTED)是最稳妥方式,它会在获取连接后、执行前自动设置 - PHP PDO 需在
new PDO()后立即执行$pdo->exec("SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED"),不能依赖配置文件(除非 PDO 连接字符串加?init_command=...) - 连接池(如 HikariCP)中,连接复用可能导致隔离级别“残留”,建议在获取连接后显式重置
最易忽略的一点:InnoDB 在 READ COMMITTED 下,普通 SELECT 不加锁,但 UPDATE/DELETE 仍只锁匹配行——这和 REPEATABLE READ 的间隙锁行为完全不同,切换后务必验证写逻辑是否仍满足业务一致性要求。











