MySQL 核心原理与高频面试题
在数据库常见面试问题中,MySQL 无疑是考察频率最高的部分。面试官往往不仅关注 SQL 语句的编写,更侧重于底层存储引擎、索引结构以及事务隔离级别的深入理解。以下整理了最具代表性的三个核心考点。
为什么 MySQL 使用 B+树 而不是 B树 或 哈希?
B+树是 InnoDB 存储引擎默认索引结构。相较于 B树,B+树 的非叶子节点只存储键值,不存储数据,这使得单个节点能容纳更多的索引项,从而降低了树的高度,减少了磁盘 I/O 次数。
- ⚡ 范围查询优势: B+树 叶子节点通过双向链表连接,非常适合范围查询(Range Scan),而 B树 需要进行中序遍历,效率较低。
- ⚡ 查询稳定性: B+树 的所有查询都必须走到叶子节点,性能更加稳定。
- ⚡ 哈希索引局限: Hash 索引仅适用于等值查询,无法支持范围查询和排序,且存在哈希冲突问题。
| 特性 | B+树 | B树 | Hash |
|---|---|---|---|
| 非叶子节点存储 | 仅键值 | 键值+数据 | N/A |
| 范围查询 | 支持 (高效) | 支持 (较慢) | 不支持 |
| 排序性能 | 天然有序 | 需额外排序 | 无序 |
事务隔离级别与并发问题
数据库常见面试问题中,事务隔离级别是必考题。SQL 标准定义了四种隔离级别,MySQL 默认采用 Repeatable Read (可重复读)。
- Read Uncommitted (读未提交): 会出现脏读、不可重复读、幻读。
- Read Committed (读已提交): Oracle 默认。解决脏读,但仍存在不可重复读和幻读。
- Repeatable Read (可重复读): MySQL 默认。通过 MVCC 解决脏读和不可重复读,通过 Next-Key Lock 解决大部分幻读。
- Serializable (串行化): 最高级别,解决所有并发问题,但性能极差,不推荐使用。
注:在 RR 级别下,MySQL 通过 MVCC(多版本并发控制)和 Gap Lock(间隙锁)机制来保证事务的隔离性。
MGR 与 Binlog 的协同工作
Binlog 是 MySQL 服务端产生的日志,记录所有修改数据的 SQL 语句。它与 InnoDB 的 Redo Log 和 Undo Log 共同构成了 MySQL 的事务持久性和原子性基础。
- ⚙️ Redo Log: InnoDB 引擎特有,物理日志,保证崩溃恢复(Crash-Safe)。
- ⚙️ Undo Log: 逻辑日志,用于事务回滚和 MVCC 实现。
- ⚙️ Binlog: 逻辑日志,用于主从复制和数据恢复。
在 MGR(MySQL Group Replication)中,Binlog 还承担着组内成员间状态同步的重要职责。
Redis 缓存策略与数据库常见面试问题
随着分布式系统的普及,Redis 已成为数据库常见面试问题中不可或缺的一环。面试官通常关注缓存穿透、击穿、雪崩的解决方案,以及 Redis 与数据库的双写一致性。
缓存穿透
指查询一个根本不存在的数据,缓存和数据库中都没有,导致每次请求都打到 DB。解决方案:布隆过滤器(Bloom Filter)或缓存空对象。
缓存击穿
指某个热点 Key 在过期瞬间,大量请求同时到达,导致 DB 压力激增。解决方案:设置热点 Key 永不过期,或使用互斥锁(Mutex Key)。
缓存雪崩
指大量 Key 同时过期 或 Redis 宕机。解决方案:过期时间加随机值,使用 Redis 集群高可用架构。
双写一致性
先更新数据库,再删除缓存(而非更新缓存)。若删除失败,需通过消息队列重试。推荐使用 Canal 订阅 Binlog 异步更新缓存。
SQL 性能优化实战
在实际工作中,SQL 优化是数据库常见面试问题中的实操重点。以下提供几个典型的优化场景及示例。
1. 索引失效的场景
- ⚡ 对索引列进行函数运算或表达式计算。
- ⚡ 隐式类型转换(如字符串不加引号)。
- ⚡ 模糊查询以 % 开头(如 LIKE '%abc')。
- ⚡ OR 条件中,若有一个字段无索引,则整体索引失效。
2. 执行计划分析示例
使用 EXPLAIN 关键字分析 SQL 执行效率,重点关注 type、key、rows 和 Extra 字段。
EXPLAIN SELECT FROM orders WHERE user_id = 1001 AND status = 'paid';
| 字段 | 说明 | 理想值 |
|---|---|---|
| type | 访问类型,性能从好到坏:system > const > eq_ref > ref > range > index > all | ref 或 range |
| key | 实际使用的索引 | 非 NULL |
| rows | 预估扫描行数 | 越小越好 |
| Extra | 额外信息 | 避免 Using filesort 和 Using temporary |
数据库架构设计与分库分表
当数据量达到千万级甚至亿级时,单机 MySQL 难以承载,分库分表成为数据库常见面试问题中的高级考点。
将用户表、订单表、商品表等拆分到不同的数据库中,解决单库连接数瓶颈和热点数据问题。
将单张表的数据按照某种规则(如 user_id % 16)分散到多个表中。常用中间件:ShardingSphere、MyCat。
主库负责写,从库负责读。通过 Binlog 同步数据,提升读吞吐量。注意主从延迟问题。
分库分表的挑战
- ⚡ 跨库 Join: 避免跨库关联查询,尽量通过冗余字段或应用层组装数据。
- ⚡ 全局 ID: 使用雪花算法(Snowflake)生成唯一 ID,避免主键冲突。
- ⚡ 分布式事务: 使用 Seata 等框架保证数据一致性,或采用最终一致性方案(如本地消息表)。