• 面试官灵魂拷问:MySQL 事务隔离与 MVCC,你真的懂吗?

面试官灵魂拷问:MySQL 事务隔离与 MVCC,你真的懂吗?

2025-06-17 08:37:03 栏目:宝塔面板 2 阅读

一、你是否也有这些痛点?

面试官:小徐,你在项目里用过事务吗?

小徐:用过啊,Spring的@Transactional用得很熟。

面试官:那你能说说MySQL的事务隔离级别和MVCC吗?它们是怎么保证数据一致性的?

小徐:……(沉默)

是不是很熟悉? 你是不是也遇到过这些场景:

  • 明明加了事务,数据还是被“脏读”了?
  • 事务隔离级别一堆名词,读未提交、可重复读、幻读,傻傻分不清楚?
  • 面试官一问MVCC,脑子里只剩下“多版本并发控制”这几个字?

别急,今天我们就用一场“灵魂拷问式”对话,把事务的隔离级别和MVCC讲明白,彻底搞懂它们的底层原理和实际应用!

二、问题1:事务是什么?

面试官:小徐,你先说说,事务是什么?

我:事务是一组操作,要么都成功,要么都失败,用来保证数据的一致性。

面试官点点头,又问:那事务的四大特性你还记得吗?

我:当然。

  • 原子性(Atomicity):事务中的操作要么全部成功,要么全部失败。
  • 一致性(Consistency):事务执行前后,数据都必须保持一致。
  • 隔离性(Isolation):并发事务之间互不干扰。
  • 持久性(Durability):事务提交后,结果永久保留。

面试官满意地点头:那我们就重点聊聊“隔离性”。你能说说MySQL支持的事务隔离级别吗?分别会出现哪些并发问题?

三、问题2:什么是事务的隔离级别?

我:隔离性听起来简单:别人操作数据库的时候我别看到就行了。

但真要实现“互不干扰”,成本非常高。所以数据库给了我们四个等级的隔离级别,让我们按需选择:

隔离级别

脏读

不可重复读

幻读

读未提交(RU)


读已提交(RC)

×

可重复读(RR)

×

×

串行化(SERIAL)

×

×

×

面试官:你说说,什么是脏读、不可重复读和幻读?

我:假设银行有个账户A,余额是100元。

脏读(Dirty Read):读到了别人还没提交的数据。

不可重复读(Non-repeatable Read):两次读取结果不一致,就是不可重复读。

幻读(Phantom Read):T1觉得自己出现“幻觉”了,明明查过只有1条,怎么又冒出一条?这就叫幻读。

面试官:MySQL 默认是什么隔离级别?

我:MySQL 默认是 Repeatable Read(可重复读)隔离级别。

面试官:那你知道,MySQL 的隔离级别到底是通过什么实现的吗?

我:MySQL 的隔离级别,主要是通过两大机制来实现的:

  • 一是锁机制
  • 二是 MVCC(多版本并发控制)。

面试官:不错,能具体说说,什么是MVCC吗?

四、问题3:MVCC 到底是怎么一回事?

我:MVCC,全称 Multi-Version Concurrency Control,多版本并发控制。

通俗讲:每次读操作,都能读到一个“历史快照”,就像你拍了张数据的照片,别人改不影响你。

面试官:那MVCC的原理是什么?它是怎么做到让每个事务看到自己的快照的?

1. MVCC实现原理

我:MVCC的实现主要依赖于以下几个关键点:

(1) 隐藏字段:每一行数据在InnoDB存储引擎下,都会有两个隐藏字段:

  • 创建版本号(DB_TRX_ID):记录插入或最后一次修改该行的事务ID。
  • 删除版本号(DB_ROLL_PTR):记录删除该行的事务ID(如果没被删除则为NULL)。

(2) Undo Log(回滚日志)

  • 当数据被修改(UPDATE/DELETE)时,InnoDB会把旧版本的数据写入Undo Log。
  • 这样,其他事务如果需要读取历史版本的数据,就可以通过Undo Log“回溯”到对应的快照。

(3) Read View(可见性视图)

  • 每个事务在启动时,会生成一个Read View,记录当前活跃的事务ID列表。
  • 事务只能“看到”在自己启动前已经提交的数据版本。

(4) 快照读与当前读

  • 快照读(Snapshot Read):普通的SELECT语句,走MVCC,不加锁。
  • 当前读(Current Read):带有锁的操作(如SELECT ... FOR UPDATE、UPDATE、DELETE),需要读取最新版本并加锁。

2. MVCC的工作流程

我们通过一个例子解释下:

(1) 事务A开始,生成快照

事务A启动,记录当前数据的快照(此时name=张三,age=10)。

(2) 事务B启动,把name从“张三”改为“李四”。

数据库不会直接覆盖原数据,而是把原来的数据(张三)写入undo log,生成一个新版本(李四),并更新事务ID和roll_pointer。

(3) 事务B未提交时,事务A读取数据

  • 事务A此时去读这条数据,发现有新版本(李四,10),但这个版本的事务ID比A大,且还没提交。
  • 根据MVCC规则,A不能看到B未提交的修改。
  • 于是A会通过roll_pointer去undo log里找历史版本,找到自己快照时的数据(张三,10),返回给A。

(4) 事务B提交:事务B提交后,新的数据版本(李四)正式生效。

(5) 事务C启动时,快照中已经包含了B的提交。此时C读取数据,看到的就是最新的(李四,10)。

面试官:MVCC能解决所有并发问题吗?幻读怎么处理?

五、问题4:MVCC能解决所有并发问题吗?

我:MVCC不能解决所有并发问题。它主要解决了“脏读”、“不可重复读”等问题,让读写操作互不阻塞,提高了并发性能。但MVCC无法解决“幻读”问题。

MVCC适用的隔离级别:

  • 读已提交(RC)和可重复读(RR):都用MVCC实现快照读。
  • 读未提交(RU):不走MVCC,直接读最新数据。
  • 串行化(SERIALIZABLE):加锁,性能最差。

面试官点头:那MySQL怎么避免幻读?

我:MVCC本身无法彻底解决幻读,因为MVCC只保证了“行级”的多版本,并不能控制“新行的出现或消失”。

在MySQL InnoDB中,解决幻读主要依赖于“间隙锁(Gap Lock)”:

  • 在可重复读(REPEATABLE READ)隔离级别下,InnoDB会在范围查询时加上间隙锁,防止其他事务在查询范围内插入新行,从而避免幻读。
  • 如果是读已提交(READ COMMITTED),则不会加间隙锁,幻读依然可能发生。

面试官:Perfect!看来你对MVCC了解的很深入了,下周就来公司报道吧。

六、总结

所谓的 MVCC,指的是在 READ COMMITTED 和 REPEATABLE READ 这两种隔离级别下,事务在执行普通 SELECT 操作时,通过访问记录的版本链,实现不同事务间的读-写、写-读操作可以并发进行,从而提升系统性能。

实用建议:什么时候选哪种隔离级别?

  • 如果对一致性要求极高(如转账系统):用 SERIALIZABLE,但注意性能下降严重
  • 一般系统默认  REPEATABLE READ 就够用了,配合MVCC性能好、隔离也够
  • 如果你只想防止脏读,但可以接受不可重复读,READ COMMITTED 可以提高并发
  • 可别图省事用最低的 READ UNCOMMITTED,脏读太危险,几乎没人用。

本文地址:https://www.yitenyun.com/290.html

搜索文章

Tags

数据库 API FastAPI Calcite 电商系统 MySQL 数据同步 ACK 双主架构 循环复制 Web 应用 异步数据库 Deepseek 宝塔面板 Linux宝塔 Docker JumpServer JumpServer安装 堡垒机安装 Linux安装JumpServer 生命周期 序列 核心机制 esxi esxi6 root密码不对 无法登录 web无法登录 Windows Windows server net3.5 .NET 安装出错 宝塔面板打不开 宝塔面板无法访问 SSL 堡垒机 跳板机 HTTPS Windows宝塔 Mysql重置密码 无法访问宝塔面板 HTTPS加密 查看硬件 Linux查看硬件 Linux查看CPU Linux查看内存 连接控制 机制 ES 协同 scp Linux的scp怎么用 scp上传 scp下载 scp命令 修改DNS Centos7如何修改DNS Serverless 无服务器 语言 日志文件 MIXED 3 Oracle 处理机制 RocketMQ 长轮询 配置 防火墙 服务器 黑客 存储 Spring SQL 动态查询 Rsync HexHub 加密 场景 MySQL 9.3 缓存方案 缓存架构 缓存穿透 Linux 安全 网络架构 工具 网络配置 Canal 聚簇 非聚簇 索引 开源 PostgreSQL 存储引擎 Redis 自定义序列化 线上 库存 预扣 数据 业务 信息化 智能运维 架构 InnoDB 响应模型 B+Tree ID 字段 Redis 8.0 分页查询 数据库锁 单点故障 分库 分表 DBMS 管理系统 ​Redis 机器学习 推荐模型 监控 prometheus Alert GreatSQL Hash 字段 共享锁 云原生 Netstat Linux 服务器 端口 openHalo 查询 AI 助手 SQLark 技术 排行榜 排序 容器化 SVM Embedding 虚拟服务器 虚拟机 内存 SpringAI 优化 万能公式 OB 单机版 Doris SeaTunnel 不宕机 自动重启 运维 Postgres OTel Iceberg 电商 系统 崖山 新版本 高可用 OAuth2 Token RDB AOF 数据集成工具 向量数据库 大模型 SQLite-Web SQLite 数据库管理工具 缓存 sqlmock PostGIS • 索引 • 数据库 Entity 开发 人工智能 推荐系统 IT运维 redo log 重做日志 Redka MongoDB 容器 sftp 服务器 参数 向量库 Milvus ZODB Python 分页 数据结构 EasyExcel MySQL8 Web 同城 双活 数据备份 分布式架构 分布式锁​ 聚簇索引 非聚簇索引 LRU StarRocks 数据仓库 Testcloud 云端自动化 MVCC 微软 SQL Server AI功能 数据脱敏 加密算法 数据类型 Ftp AIOPS IT 流量 mini-redis INCR指令 池化技术 连接池 磁盘架构 Web 接口 1 PG DBA 悲观锁 乐观锁 Caffeine CP MCP 开放协议 窗口 函数 部署 R2DBC RAG HelixDB 原子性 字典 模型 单线程 线程 速度 服务器中毒 事务隔离 dbt 数据转换工具 引擎 性能 连接数 网络故障 双引擎 QPS 高并发 主库 JOIN 对象 Order SSH 网络 Pottery InfluxDB 工具链 INSERT COMPACT Undo Log LLM Go 数据库迁移 Redisson 锁芯 高效统计 今天这篇文章就跟大家 频繁 Codis Crash 代码 发件箱模式 优化器 List 类型 Recursive filelock 大表 业务场景 管理口 意向锁 记录锁 事务同步 分布式 集中式 传统数据库 向量化 线程安全 仪表盘 分页方案 排版 UUIDv7 主键 订单 启动故障 核心架构 订阅机制 Pump 读写 数据分类