Skip to content

数据库原理

本篇讲跨数据库的通用原理:事务、索引、锁、MVCC。MySQL 的具体优化见后端篇的 MySQL 专题。

Q1: 事务的 ACID 特性? 「🟢 校招/初级」

考察点:数据库事务的基本素养。

参考答案

  • 原子性(A):事务要么全做要么全不做,靠 undo log 实现回滚。
  • 一致性(C):事务前后数据满足约束,是最终目标。
  • 隔离性(I):并发事务互不干扰,靠锁 + MVCC 实现。
  • 持久性(D):提交后不丢失,靠 redo log 的 WAL 机制保证。

追问延伸

  • 一致性是其他三个特性"保证"出来的吗?
  • 崩溃恢复时 redo log 和 undo log 各起什么作用?

Q2: 事务隔离级别有哪些?各自解决什么问题? 「🟡 中级」

考察点:并发异常的三件套与隔离级别的对应关系。

参考答案

隔离级别脏读不可重复读幻读
读未提交可能可能可能
读已提交(RC)不会可能可能
可重复读(RR)不会不会InnoDB 基本解决
串行化不会不会不会
  • 脏读:读到别人未提交的数据;不可重复读:同一事务两次读结果不同(被别人 UPDATE);幻读:范围查询两次结果行数不同(被别人 INSERT)。
  • MySQL InnoDB 默认 RR,Oracle/PostgreSQL 生态常用 RC。

追问延伸

  • InnoDB 在 RR 级别下是怎么解决幻读的?(快照读靠 MVCC,当前读靠间隙锁)
  • 为什么很多互联网公司把隔离级别降为 RC?

Q3: 什么是 MVCC? 「🟡 中级」

考察点:理解"读写不互斥"的实现,高频深水区。

参考答案

  • MVCC(多版本并发控制):每行数据保存多个版本,读操作读某个历史快照,从而读不加锁、读写不阻塞。
  • InnoDB 实现:每行有隐藏字段(事务 ID、回滚指针)+ undo log 版本链;事务开始时生成 ReadView,按可见性规则决定读哪个版本。
  • RC 与 RR 的区别:RC 每次 SELECT 都生成新 ReadView,所以能读到别人新提交的数据;RR 只在第一次读时生成。

追问延伸

  • 快照读和当前读(SELECT ... FOR UPDATE)的区别?
  • MVCC 能完全替代锁吗?什么场景还是要锁?

Q4: 索引为什么能加速查询?有什么代价? 「🟡 中级」

考察点:索引的收益与成本权衡(B+ 树细节见"树与图"篇)。

参考答案

  • 收益:把全表扫描(O(n))变成树查找(O(log n)),并支持有序性带来的范围查询、排序、覆盖索引优化。
  • 代价:占用磁盘空间;写入时要维护索引树(增删改变慢);索引过多影响优化器选择。
  • 原则:高区分度、查询条件、排序分组字段建索引;低区分度字段(性别)单独建意义不大。

追问延伸

  • 什么是覆盖索引?
  • 联合索引 (a, b, c),查询条件只有 b 能用到索引吗?

Q5: 数据库的锁有哪些分类? 「🟡 中级」

考察点:锁粒度和锁类型的完整图谱。

参考答案

  • 按粒度:表锁(开销小、并发低)、行锁(开销大、并发高)、页锁(介于两者)。
  • 按模式:共享锁(S,读)、排他锁(X,写);意向锁用于快速判断表上是否有行锁。
  • InnoDB 行锁的三种算法:记录锁(锁一行)、间隙锁(锁区间,防幻读)、临键锁(记录+间隙,RR 下默认)。
  • 注意:行锁是加在索引上的,走不了索引会退化为表锁。

追问延伸

  • 两个事务更新同一行为什么可能死锁?
  • RC 级别下间隙锁还存在吗?

Q6: 什么是 WAL(预写日志)?为什么先写日志再写数据页? 「🟡 中级」

考察点:数据库持久性与性能的平衡设计。

参考答案

  • 修改数据时先把变更写入日志(顺序写),再择机把脏页刷盘(随机写)。
  • 好处:顺序写日志比随机写数据页快得多;崩溃后可以用日志重放恢复(redo),保证已提交事务不丢。
  • 本质是用顺序写换随机写,同时解决持久性和性能问题。

追问延伸

  • 刷脏页的时机有哪些?
  • Redis 的 AOF 和 WAL 思路有什么相似之处?