mysql默认事务隔离级别是repeatable read,但innodb通过间隙锁+行锁实现“防幻读”,并非标准快照隔离,仍可能因间隙锁导致阻塞或死锁。

MySQL默认事务隔离级别是啥,REPEATABLE READ真能防幻读吗
InnoDB 默认用 REPEATABLE READ,但它的“防幻读”靠的是间隙锁(gap lock)+ 行锁,不是标准 SQL 定义的快照隔离。这意味着:同一事务里两次 SELECT 看到的行数可能一样,但如果你执行 INSERT 或 UPDATE 涉及间隙,就可能被阻塞甚至死锁。
常见错误现象:SELECT ... FOR UPDATE 在范围查询时莫名卡住,或者两个事务同时插入同一条不存在的记录却报死锁——这往往就是间隙锁在起作用,而你根本没意识到。
-
READ COMMITTED下间隙锁会被禁用,幻读可能发生,但写冲突变少; - 想彻底避免幻读又不想锁太狠?得配合
SELECT ... FOR UPDATE+ 明确主键条件,避开范围扫描; - 线上改隔离级别要小心:
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED只对当前会话生效,应用层若依赖默认行为,可能突然出现数据不一致。
MyISAM 根本不支持事务,谈隔离级别就是自欺欺人
MyISAM 没有 START TRANSACTION、没有回滚、没有隔离级别概念。它只提供表级锁,INSERT、UPDATE、DELETE 都会锁整张表。所谓“MyISAM 支持 READ UNCOMMITTED”纯属误解——它连事务都没有,压根不走事务路径。
使用场景只剩一种:极低并发、只读为主、数据量小、且你能接受任意时刻 SELECT 可能读到一半写入的脏页(比如日志归档表)。只要业务里有任何“先查后更”逻辑,MyISAM 就不该出现在生产库。
- 迁移老系统时发现建表语句里写着
ENGINE=MyISAM,别急着改成 InnoDB——先确认外键、全文索引、压缩等特性是否被隐式依赖; -
SHOW CREATE TABLE查出来的引擎类型,比配置文件里的默认引擎更真实; - MyISAM 表执行
BEGIN不报错,但后续ROLLBACK无效,这种“静默失效”最容易埋坑。
InnoDB 行锁为啥有时会升级成表锁
InnoDB 行锁生效的前提是:查询条件命中索引。如果 WHERE 用的是非索引字段,或者用了函数、隐式类型转换,优化器会放弃走索引,最终触发全表扫描+锁全表。
典型错误现象:一条 UPDATE user SET status=1 WHERE phone='138...' 执行超慢还堵住其他写操作——查 EXPLAIN 发现 type=ALL,再看 phone 字段没索引,或类型是 VARCHAR 却传了数字,触发隐式转换。
- 判断是否真用了行锁?执行
SELECT * FROM performance_schema.data_locks(MySQL 8.0+),看LOCK_DATA是具体值还是NULL(表锁标志); - 联合索引要注意最左前缀:
(a,b,c)上查WHERE b=1 AND c=1仍然会锁全表; -
UPDATE ... LIMIT 1不保证只锁一行——如果没有合适索引,InnoDB 仍可能扫描多行再截断,这些中间行也会被加锁。
查看和修改当前事务隔离级别的正确姿势
别信网上抄来的 SELECT @@tx_isolation,MySQL 5.7.20+ 已弃用,该用 SELECT @@transaction_isolation。全局设置写进 my.cnf 的 transaction_isolation 项,但注意:已存在的连接不会自动继承新值。
性能影响很实际:从 REPEATABLE READ 切到 READ COMMITTED 后,MVCC 版本链清理更积极,ibdata1 膨胀速度下降,但一致性读的开销略升——因为每次读都要找最新已提交版本,而不是复用事务开启时的快照。
- 应用连接池初始化时,建议显式执行
SET SESSION TRANSACTION ISOLATION LEVEL ...,别依赖服务端默认; -
SELECT @@global.transaction_isolation和SELECT @@session.transaction_isolation结果可能不同,调试时务必分清作用域; - Docker 环境下改配置,记得检查容器启动时是否挂载了正确的
my.cnf,有时候镜像内置配置会覆盖你挂载的。
事务隔离不是开关按钮,是锁、版本、索引、查询写法共同作用的结果。同一个 SELECT,换一个条件写法,锁的粒度和范围可能天差地别。











