MySQL 事务隔离级别与 MVCC 深度解析

引言

从并发问题出发,彻底理解 MySQL 为什么这样设计事务隔离

一、为什么需要事务隔离级别?

在并发数据库系统中,多个事务同时读写同一份数据是常态。如果不加任何控制,就会引发各种数据一致性问题,例如:

  • 一个事务读到了另一个事务尚未提交的数据
  • 同一事务中,两次查询结果不一致
  • 查询过程中"凭空多出"一些记录

这些问题统称为并发读问题 ,而 事务隔离级别(Isolation Level),正是数据库在「性能」和「一致性」之间做出的不同权衡方案。

二、MySQL 支持的四种事务隔离级别

SQL 标准定义了四种隔离级别,MySQL(InnoDB)全部支持。

读未提交(Read Uncommitted)

特点
  • 一个事务可以读到另一个事务尚未提交的数据
问题
  • 脏读(Dirty Read)
示例
复制代码
事务A:update account set balance = 0 where id = 1; (未提交)
事务B:select balance from account where id = 1; → 读到 0
事务A:rollback;

B 读到的数据是从未真正存在过的

评价
  • 几乎不加任何隔离
  • 实际生产环境基本不用

读已提交(Read Committed)

Oracle 的默认隔离级别

特点
  • 只能读到已提交的数据
  • 每次查询都会读取最新已提交版本
解决的问题
  • 避免脏读
仍然存在的问题
  • 不可重复读(Non-repeatable Read)
示例
复制代码
事务A:select balance → 100
事务B:update balance = 200; commit;
事务A:select balance → 200

同一个事务中,两次读取结果不同

可重复读(Repeatable Read)⭐

MySQL InnoDB 默认隔离级别

特点
  • 同一个事务中,多次读取同一行记录结果一致
  • 基于 MVCC(多版本并发控制)
能解决的问题
  • 脏读
  • 不可重复读
可重复读下,事务 A 提交的数据,事务 B 能看到吗?

分两种情况:

情况一:普通快照读(一致性读)
复制代码
select * from table;
  • 看不到
  • 使用事务开始时生成的 Read View
情况二:当前读(锁定读)
复制代码
select * from table for update;
select * from table lock in share mode;
  • 可以看到
  • 直接读取最新版本

这正是 MySQL 可重复读与其他数据库的核心区别之一

串行化(Serializable)

特点
  • 所有事务串行执行
  • 强制加锁(行锁 / 表锁)
实现方式
  • 一种实现方式:读操作也加行级锁
  • 完全避免并发问题
缺点
  • 并发性能极差
  • 几乎不用于高并发系统

三、可重复读真的解决了幻读吗?

什么是幻读(Phantom Read)?

复制代码
事务A:select count(*) from orders where price > 100;
事务B:insert into orders values (..., price=200); commit;
事务A:再次 select count(*) → 结果变多

多出"幻影行"

MySQL 的"特殊之处"

严格来说:

可重复读 + MVCC 并不能完全解决幻读

  • MVCC 只能保证已存在记录的版本一致
  • 对于 新插入的记录,MVCC 无能为力

MySQL 如何"防止幻读"?

方案一:锁定读(推荐)
复制代码
select * from orders where price > 100 for update;
  • 会加 Next-Key Lock(记录锁 + 间隙锁)
  • 锁住一个范围
  • 阻止其他事务插入新记录

从结果上看:幻读被避免

方案二:串行化隔离级别
  • 本质是用锁解决
  • 代价太高

四、MVCC:多版本并发控制的核心原理

MySQL 高并发性能的关键

MVCC 解决了什么问题?

  • 避免读写冲突
  • 提高并发性能
  • 实现 非阻塞读

MVCC 的核心组件

(1)隐藏字段
  • trx_id:最后一次修改该行的事务 ID

  • roll_pointer:指向 undo log

(2)Undo Log
  • 保存数据的历史版本

  • 用于事务回滚 & MVCC

(3)Read View
  • 决定"当前事务能看到哪些版本"

一致性读流程(简化)

复制代码
当前事务 → 生成 Read View
        → 判断记录 trx_id 是否可见
        → 不可见 → 通过 undo log 找历史版本

MVCC + 锁的协作关系

场景 机制
普通 select MVCC
select for updateUpdate
update / delete
防止幻读 Next-Key Lock

MVCC 解决"读一致性",锁解决"写冲突"

总结

MySQL 默认使用 可重复读隔离级别

MVCC 保证了:

  • 非阻塞读
  • 高并发性能

幻读并不是完全由 MVCC 解决

锁定读 + Next-Key Lock 才是幻读的真正终结者

MySQL 的事务模型,本质是:

MVCC + 锁机制的精妙组合

相关推荐
javachen__13 分钟前
mysql系统级文件损坏修复
数据库·mysql
麦麦鸡腿堡29 分钟前
MySQL_合计/统计函数
数据库·mysql
五阿哥永琪37 分钟前
MySQL面试题 事务实现全解析:Undo Log、Redo Log、锁与 MVCC 协同机制详解
数据库·mysql
txinyu的博客37 分钟前
MySQL 学过但是全忘了?15min帮你快速复习
数据库·mysql
千层冷面11 小时前
数据库分库分表
java·数据库·mysql·oracle
清风拂山岗 明月照大江13 小时前
MySQL运维
运维·数据库·mysql
进击的CJR13 小时前
redis哨兵实现主从自动切换
mysql·ffmpeg·dba
crossaspeed13 小时前
MySql三大日志——(八股)
数据库·mysql
墨香幽梦客16 小时前
数据库选型对比:MySQL、Oracle与PostgreSQL的企业应用场景分析
数据库·mysql·oracle
清风拂山岗 明月照大江17 小时前
MySQL进阶
数据库·sql·mysql