少女祈祷中...
NEKO
知识库

mysql 事务面试要点

一、基础概念类

Q1:什么是事务?ACID 分别是什么?

  • 事务是一组要么全部成功、要么全部失败的操作。

  • A 原子性:操作不可分割,靠 undo log 回滚。

  • C 一致性:数据从一个合法状态到另一个合法状态,是目的,靠其他三者共同保证。

  • I 隔离性:并发事务互不干扰,靠锁 + MVCC。

  • D 持久性:提交后永久生效,靠 redo log。

Q2:ACID 分别靠什么实现?

特性

实现机制

原子性

undo log

持久性

redo log

隔离性

锁 + MVCC

一致性

是目的,由 A、I、D 共同保障

Q3:事务的隔离级别有哪些?分别解决什么问题?

隔离级别

脏读

不可重复读

幻读

读未提交 RU

可能

可能

可能

读已提交 RC

不会

可能

可能

可重复读 RR

不会

不会

InnoDB 基本不会

串行化

不会

不会

不会

  • MySQL InnoDB 默认 RR。

  • RR 下靠 MVCC + 间隙锁,很大程度上避免了幻读。

Q4:脏读、不可重复读、幻读的区别?

  • 脏读:读到别的事务未提交的数据。

  • 不可重复读:同一事务内两次读同一行,结果不同(别的事务改了并提交)。

  • 幻读:同一事务内两次范围查询,第二次多出或少了几行(别的事务插入/删除并提交)。

Q5:RC 和 RR 的区别?

  • 都用 MVCC,区别在 ReadView 生成时机:

    • RC:每次快照读都生成新的 ReadView,所以能读到别人已提交的新数据。

    • RR:只在第一次快照读时生成 ReadView,之后复用,所以可重复读。

  • RR 额外用间隙锁防当前读的幻读。

二、MVCC 类

Q6:什么是 MVCC?

  • 多版本并发控制。通过保存数据的多个版本,让读不加锁、读写不冲突。

  • 核心三要素:隐藏字段(trx_id、roll_pointer)、undo log 版本链、ReadView。

Q7:MVCC 的实现原理?

  1. 每行有隐藏列 DB_TRX_ID(最后修改的事务 ID)和 DB_ROLL_PTR(指向 undo log 的指针)。

  2. 每次修改,旧版本写入 undo log,形成版本链。

  3. 读时生成 ReadView,记录当前活跃事务 ID 列表。

  4. 沿版本链找第一个对当前 ReadView 可见的版本。

Q8:ReadView 的可见性判断规则?

设 m_ids 为活跃事务列表,min_trx_id 为最小值,max_trx_id 为下一个待分配 ID:

  • trx_id < min_trx_id:已提交,可见。

  • trx_id >= max_trx_id:ReadView 之后才开启,不可见。

  • min_trx_id <= trx_id < max_trx_id:

    • 在 m_ids 中 → 活跃未提交,不可见;

    • 不在 m_ids 中 → 已提交,可见。

Q9:当前读和快照读的区别?

  • 快照读:普通 SELECT,走 MVCC,读历史版本,不加锁。

  • 当前读:SELECT ... FOR UPDATE、LOCK IN SHARE MODE、UPDATE、DELETE、INSERT,读最新版本并加锁。

Q10:RR 下幻读完全解决了吗?

  • 快照读:靠 MVCC,不会幻读。

  • 当前读:靠间隙锁/临键锁,基本不会幻读。

  • 但特殊场景(先快照读、再当前读)可能仍出现,所以严格说不是 100% 杜绝。

三、锁与并发类

Q11:InnoDB 有哪些锁?

  • 按粒度:全局锁、表锁、行锁。

  • 行锁类型:

    • 记录锁:锁一条记录。

    • 间隙锁:锁一个区间,防插入。

    • 临键锁

      • 命中记录:临键锁(Next-Key Lock),可能还包含一个额外的间隙锁。普通索引等值查询命中时,加的是 临键锁(Next-Key Lock),即"记录锁 + 前面的间隙锁"的组合,并且还会向后多锁一个间隙(下一个临键锁的间隙部分),以防止插入。

      • 未命中:间隙锁。

    • 插入意向锁:插入前加的间隙锁。

  • 表级:意向共享锁、意向排他锁、MDL。

Q12:RR 下加锁规则?

  • 唯一索引等值命中:记录锁。

  • 唯一索引等值未命中:间隙锁。

  • 普通索引等值:临键锁 + 间隙锁。

  • 范围查询:临键锁。

  • 没走索引:锁全表。

Q13:死锁怎么产生?如何排查和避免?

  • 产生:两个事务互相持有对方需要的锁,循环等待。

  • 排查:SHOW ENGINE INNODB STATUS,看 LATEST DETECTED DEADLOCK。

  • 避免:

    • 按固定顺序访问资源;

    • 小事务、快提交;

    • 走索引,避免锁太多行;

    • 设置 innodb_lock_wait_timeout。

Q14:乐观锁和悲观锁的区别?

  • 悲观锁:先加锁再操作,SELECT ... FOR UPDATE,适合冲突多。

  • 乐观锁:不加锁,提交时用版本号/CAS 校验,适合冲突少。

四、日志与持久化类

Q15:redo log、undo log、binlog 的区别?

日志

层次

类型

作用

redo log

InnoDB

物理

崩溃恢复、持久性

undo log

InnoDB

逻辑

回滚、MVCC

binlog

Server

逻辑

主从复制、归档

Q16:什么是两阶段提交?为什么需要?

  • 流程:写 redo log(prepare)→ 写 binlog → 提交 redo log(commit)。

  • 目的:保证 redo log 和 binlog 逻辑一致,崩溃恢复时不会出现主从数据不一致。

  • 如果不用两阶段提交,可能出现 redo 有、binlog 没有,或反过来。

Q17:崩溃恢复时怎么判断用 redo 还是 binlog?

  • redo 处于 prepare 且 binlog 完整 → 提交。

  • redo 处于 prepare 且 binlog 不完整 → 回滚。

  • redo 已 commit → 直接恢复。

Q18:innodb_flush_log_at_trx_commit 和 sync_binlog 怎么取舍?

  • =1:每次提交都刷盘,最安全,性能最低。

  • =0/2:性能高,但崩溃可能丢最近事务。

  • 要求不丢:两个都设 1。

五、实战与设计类

Q19:长事务有什么危害?

  • 占用 undo log,版本链变长,影响 MVCC 查询。

  • 持有锁时间长,阻塞其他事务,易死锁。

  • 占用连接和内存,可能撑大 redo。

  • 主从延迟可能加大。

Q20:如何避免长事务?

  • 控制事务粒度,快进快出。

  • 避免在事务里做远程调用、批量操作。

  • 设置 innodb_lock_wait_timeout、监控 information_schema.innodb_trx。

Q21:大事务怎么处理?

  • 拆成小批次提交。

  • 用 LIMIT 分批更新/删除。

  • 避免一次性 UPDATE 全表。

Q22:RR 下 SELECT ... FOR UPDATE 没走索引会怎样?

  • 会锁全表所有行和间隙,并发急剧下降。

  • 所以当前读的 WHERE 一定要走索引。

Q23:MVCC 能解决写写冲突吗?

  • 不能。MVCC 只解决读写冲突,写写冲突仍靠锁。

  • 两个事务改同一行,后改的会阻塞等锁。

六、高频陷阱题

Q24:RR 下两次快照读之间别人插入并提交,会幻读吗?

  • 不会。快照读复用第一次的 ReadView,看不到新插入的行。

Q25:RR 下先快照读,再当前读,会幻读吗?

  • 可能。当前读读最新数据,会看到新插入的行,这就是“幻读”的边界场景。

Q26:SELECT 会加锁吗?

  • 普通 SELECT 是快照读,不加锁。

  • SELECT ... FOR UPDATE / LOCK IN SHARE MODE 是当前读,加锁。

Q27:事务提交后,undo log 立刻删除吗?

  • 不一定。undo log 还要供 MVCC 读旧版本,等没有 ReadView 需要它时才清理。

Q28:为什么 RR 是 MySQL 默认隔离级别?

  • 在主从复制 STATEMENT 格式下,RC 可能导致主从不一致,RR 更安全。

    • STATEMENT 格式下,binlog 记录的是原始 SQL 语句,从库重放这些语句。

    • 在 RC 下,间隙锁基本被禁用,并发插入/更新更容易导致主库和从库执行同一语句时看到不同的数据快照,从而产生主从不一致。

      • 经典例子:主库执行 DELETE FROM t WHERE a = 1 和 INSERT 并发,binlog 按提交顺序记录,从库重放时因缺间隙锁,执行结果可能与主库不同。

    • 在 RR 下,间隙锁能防止这类并发插入,使语句在主从上的执行效果一致,因此对 STATEMENT 复制更安全。

  • 加上 MVCC + 间隙锁,RR 在一致性和并发间取得较好平衡。

Q29:START TRANSACTION 和 BEGIN 区别?

  • 基本等价。START TRANSACTION 可加 READ ONLY、WITH CONSISTENT SNAPSHOT 等修饰。

Q30:事务的隔离级别怎么设置?

  • 会话级:SET SESSION TRANSACTION ISOLATION LEVEL ...

  • 全局:SET GLOBAL TRANSACTION ISOLATION LEVEL ...

  • 启动参数:transaction-isolation

评论