脏读在 READ UNCOMMITTED 下一定会发生,因为该隔离级别完全不加读锁也不检查写锁,SELECT 可直接读到其他事务未 COMMIT 的修改,如事务 A 更新某行未提交,事务 B 此时 SELECT 即可读到脏值。
脏读在 READ UNCOMMITTED 下为什么一定会发生
因为该隔离级别完全不加读锁,也不检查写锁,
可以直接读到其他事务尚未
的修改。比如事务 A 更新了某行但未提交,事务 B 此时执行
就能拿到这个“脏值”。
常见错误现象:
虽然不直接相关,但很多人误以为开低隔离级就能避免死锁——其实相反,
会放大并发写冲突的隐蔽性。
实操建议:
仅用于日志表、监控计数等允许误差的只读场景
MySQL 默认不启用此级别,需显式设置
ORM 框架(如 Django、MyBatis)通常不支持该级别,强行配置可能被忽略
不可重复读和幻读的区别到底在哪
不可重复读(Non-Repeatable Read)指同一事务中两次
同一行,结果不一致,原因是其他事务对该行执行了
或
并已提交;而幻读(Phantom Read)是两次
返回行数不同,原因是其他事务插入(
)或删除了满足条件的新行。
关键区别在于:不可重复读聚焦「已有行内容变化」,幻读聚焦「结果集范围变化」。
MySQL InnoDB 在
级别下通过间隙锁(Gap Lock)+ 行锁(Record Lock)阻止幻读,但仅对当前读(如
、
)生效;快照读(普通
)靠 MVCC 避免不可重复读,但本身不解决幻读——只是你“看不见”新插入的行。
实操建议:
是快照读,不会阻塞插入;但
会加临键锁(Next-Key Lock),阻止间隙插入
用
查看执行计划时,若出现
且
,说明走了覆盖索引,但间隙锁范围仍由查询条件决定,不是由是否覆盖决定
唯一索引等值查询(如
)只锁匹配行,不锁间隙;普通索引或范围查询才触发间隙锁
为什么 MySQL 默认用 REPEATABLE READ 而不是 READ COMMITTED
历史原因:早期 MySQL 主从复制依赖语句级(SBR)模式,
下同一事务内多次执行相同语句可能产生不同结果,导致主从不一致;虽然现在默认是行级复制(RBR),但兼容性与行为稳定性让
保留为默认。
MySQL(Linux)
MySQL 9.6.0是面向Linux平台的2026年创新版本,核心架构迎来重大革新。其将外键约束与级联操作从InnoDB引擎层上移至SQL层,确保所有数据变更均被完整记录至Binlog,彻底解决了CDC(变更数据捕获)与主从复制中的数据不一致难题。此外,该版本引入container_aware启动选项以原生适配容器环境,并对审计日志进行了组件化重构,为追求极致数据一致性与云原生体验的开发者提供了全新选择。
下载
性能影响明显:
需维护更长的 undo log 链(事务开启后第一个快照持续有效),高并发长事务容易撑爆
;而
每次快照读都取最新已提交版本,undo 压力小。
实操建议:
高并发短事务(如 API 请求)可考虑全局设为
,降低锁竞争和 undo 开销
变更隔离级别前务必测试 binlog 格式:
应为
,否则 SBR +
仍有风险
Spring
在 MySQL 上生效,但要注意连接池是否复用旧连接并携带旧隔离级
Serializable 怎么“彻底”解决所有并发问题
会让所有普通
自动转成
,即每次读都加共享锁,写操作则需排他锁,本质上把并发事务串行化执行。
这确实消灭了脏读、不可重复读、幻读,但也带来严重代价:并发度归零倾向,极易触发锁等待甚至死锁。例如两个事务同时执行
,再各自尝试
其中某行,就会因共享锁互斥而卡住。
实操建议:
不要在业务代码里动态设为
,它更适合离线数据校验脚本或极低频管理后台操作
若真需要强一致性,优先考虑应用层加分布式锁(如 Redis SETNX)或用乐观锁(
字段 +
)替代
中的
段会显示当前持有哪些锁,
下很容易看到大量
和
条目
事务隔离级别的选择从来不是“越严越好”,而是权衡一致性需求、性能损耗和锁行为复杂度。最容易被忽略的是:MVCC 快照读的可见性规则与锁机制是两套并行逻辑,混用时(比如一边快照读、一边
)容易误判“为什么没锁住”。
SELECTCOMMITSELECTERROR 1205 (40001): Deadlock found when trying to get lockREAD UNCOMMITTEDSET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTEDSELECTUPDATEDELETESELECTINSERTREPEATABLE READSELECT ... FOR UPDATESELECT ... LOCK IN SHARE MODESELECTSELECT * FROM t WHERE id = 1SELECT * FROM t WHERE id = 1 FOR UPDATEEXPLAINtype: indexExtra: Using where; Using indexWHERE id = ?READ COMMITTEDREPEATABLE READREPEATABLE READundo tablespaceREAD COMMITTEDREAD COMMITTEDSHOW VARIABLES LIKE 'binlog_format'ROWREAD COMMITTED@Transactional(isolation = Isolation.READ_COMMITTED)SERIALIZABLESELECTSELECT ... LOCK IN SHARE MODESELECT * FROM t WHERE status = 0UPDATESERIALIZABLEversionWHERE version = ?SHOW ENGINE INNODB STATUS\GTRANSACTIONSSERIALIZABLELOCK_MODE: SLOCK_TYPE: RECORDFOR UPDATE