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

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

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

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

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

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