mysql默认隔离级别是repeatable read,innodb在5.7+中默认使用该级别,仅对快照读防止幻读,当前读仍可能幻读,需结合索引与锁机制综合防控。

MySQL默认隔离级别就是REPEATABLE-READ
InnoDB(MySQL 5.7+ 默认存储引擎)的默认事务隔离级别是 REPEATABLE READ,不是 READ COMMITTED,也不是 SERIALIZABLE。这点和 oracle、postgresql 等主流数据库不同,容易在迁移或调优时踩坑。
验证方式很简单,在任意 MySQL 客户端执行:
select @@GLOBAL.transaction_isolation, @@session.transaction_isolation;
输出结果通常是:
| @@GLOBAL.transaction_isolation | @@SESSION.transaction_isolation |
|---|---|
| REPEATABLE-READ | REPEATABLE-READ |
注意:MySQL 5.6 及更早版本用的是 tx_isolation 变量名,命令要换成 SELECT @@GLOBAL.tx_isolation。
为什么默认选REPEATABLE-READ?它真能防幻读吗
官方文档和很多资料说 InnoDB 在 REPEATABLE READ 下“解决了幻读”,但这是有前提的——仅针对**快照读(普通 SELECT)**。而**当前读(如 SELECT … for UPDATE、UPDATE、delete)仍可能遇到幻读**,因为 InnoDB 会加间隙锁(gap lock)或 next-key lock 来阻塞插入,但这依赖索引结构和查询条件是否命中索引。
- 没走索引的 WHERE 条件 → 可能退化为表锁,或间隙锁失效 → 幻读风险高
- WHERE 条件是等值且命中唯一索引 → 间隙锁不生效 → 其他事务仍可 INSERT 相邻值,造成幻读
- 显式使用
SELECT ... LOCK IN SHARE MODE或FOR UPDATE→ 触发当前读,InnoDB 才真正尝试用锁规避幻读
所以不能只靠隔离级别“躺平”,得结合 SQL 写法、索引设计、是否需要加锁来综合判断。
怎么安全地改隔离级别?别只改 session
临时改当前会话级别很常见,比如调试时执行:
SET SESSION transaction_isolation = 'READ-COMMITTED';
但上线前如果想全局生效,必须改配置文件(如 /etc/my.cnf),否则重启后就回去了:
[mysqld] transaction_isolation = READ-COMMITTED
注意三点:
- 配置项值必须用短横线分隔(
READ-COMMITTED),不能写成下划线或大驼峰 - 改完要重启 MySQL 实例(
systemctl restart mysqld),SET GLOBAL不持久 - 某些云数据库(如阿里云 RDS、腾讯云 CDB)不开放全局变量修改权限,只能通过控制台参数模板设置
READ-COMMITTED vs REPEATABLE-READ:性能与行为差异在哪
切换隔离级别不是“越高级越好”。实际影响集中在三方面:
- MVCC 快照生成时机:
READ COMMITTED每次 SELECT 都新建 ReadView;REPEATABLE READ在事务第一次 SELECT 时建一次,后续复用 → 后者一致性更强,但长事务可能拖慢 purge 线程 - 锁范围:RC 下只对命中记录加行锁,不加间隙锁(除非外键或唯一约束检查);RR 下默认加 next-key lock → RC 并发写入冲突更少,但幻读风险明确存在
- 主从延迟敏感度:RC 下 binlog 是 statement 格式时,某些函数(如
NOW()、UUID())可能导致主从不一致;RR + ROW 格式更稳妥
真实项目中,若业务能接受“同一事务内两次 SELECT 结果可能不同”,且写多读少(如订单状态流),READ COMMITTED 往往更轻量;若强依赖事务内读一致性(如财务对账),就别动默认值。
最常被忽略的一点:应用层 ORM(如 mybatis、hibernate)可能隐式开启事务并指定隔离级别,此时数据库配置只是兜底,优先级低于代码传参。查问题前,先确认是不是框架自己设了 ISOLATION_REPEATABLE_READ 或类似值。