Appearance
数据库原理
本篇讲跨数据库的通用原理:事务、索引、锁、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 思路有什么相似之处?