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

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

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

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

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

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