第4章 数据库与SQL优化面试题:索引优化、查询计划、分库分表、高频问题与公司侧重点
大厂高频考察点:B+树原理、EXPLAIN分析、分片策略与分布式查询优化。本章提供3个核心问答卡片+1个项目案例模板,覆盖从单机索引优化到分布式分库分表的完整链路。
面试问答卡片
Q1:请解释B+树索引与哈希索引的区别,在什么场景下使用?
| 面试官考察点 | 基础原理理解、索引适用场景、MySQL引擎差异 |
| 回答框架 | 存储结构 → 查询能力 → 适用场景(Mem vs InnoDB)→ 回表/覆盖索引影响 |
| 示例回答 | B+树非叶子节点只存键,支持范围查询、排序、顺序IO;哈希索引仅等值查询且无法排序。InnoDB默认B+树适合OLTP;Memory引擎可选哈希索引适合高频等值查询。哈希索引有哈希碰撞和全表扫描风险。 |
| 可能追问 | 为什么InnoDB选择B+树而非B树?B+树如何解决IO次数问题? |
| 避坑提醒 | 不要只说“哈希更快”,要强调范围查询场景;注意MyISAM和InnoDB索引实现差异。 |
Q2:如何分析一个慢查询?请描述完整步骤。
| 面试官考察点 | 慢查询定位能力、EXPLAIN使用熟练度、SQL调优思路 |
| 回答框架 | 开启慢日志 → 获取SQL → EXPLAIN分析(type/key/rows/Extra)→ 索引或SQL改写 → 测试验证 → 线上观察 |
| 示例回答 | 先set global slow_query_log=ON,从慢日志取出SQL;执行EXPLAIN,若type=ALL且rows>10万则需加索引。关注Extra中的Using filesort或Using temporary,通过联合索引或改写JOIN消除。修改后explain验证rows减少。 |
| 可能追问 | Explain中type从好到坏的排序?Extra中Using index和Using index condition区别? |
| 避坑提醒 | 不要只提“加索引”,要具体说加什么索引(单列/联合)、顺序;优化后要回查慢日志确认效果。 |
Q3:在高并发场景下,你们是如何做分库分表的?有什么经验?
| 面试官考察点 | 分布式设计能力、分片策略、全局ID、跨库查询解决方案 |
| 回答框架 | 分片策略选择 → 分片键确定 → 全局ID生成 → 中间件选型 → 跨库查询处理 → 数据迁移方案 |
| 示例回答 | 按用户ID哈希分16库每库256表,使用雪花ID保证全局唯一且趋势递增,用ShardingSphere-JDBC透明路由。跨库查询通过汇总表或ES二次聚合实现;分页排序采用基于有序ID的游标方式避免全量扫描。 |
| 可能追问 | 分片后如何做跨库JOIN?数据倾斜怎么处理?分片键选择原则? |
| 避坑提醒 | 不要只夸分库分表好处,要主动提带来的复杂度(分布式事务、数据一致性、扩容问题);强调业务优先选择高频查询的分片键。 |
项目案例:电商订单系统读写分离与分库分表改造
| 项目名称 | 电商订单系统读写分离与分库分表改造 |
| 业务背景 | 日订单量从10万增长至500万,MySQL单库写入性能骤降,慢查询比例超过20% |
| 规模指标 | 日订单500万+,订单表数据量2亿+;QPS峰值1.5万,读多写少(8:2) |
| 技术栈 | MySQL 8.0 + ShardingSphere-JDBC + Redis + RocketMQ + Ruby on Rails(Ruby 3.0) |
| 核心难点 |
- 分片键选择:按订单ID vs 用户ID(最终选用户ID保证同用户数据聚合)
- 跨分片排序分页:采用基于有序ID的游标方式,避免全量扫描
- 全局唯一ID:使用雪花算法(workerId取自服务实例IP)
- 分布式事务:最终一致性方案,通过RocketMQ + 本地事务表保证
|
| 面试高频追问 |
- 如何保证分片后全局ID不重复且趋势递增?
- 跨库分页时怎么处理性能问题?
- 数据迁移怎样做到不停机平滑切换?
- 分片后哈希冲突导致数据倾斜如何处理?
|
| 可迁移回答话术 | “核心思路是选择业务高频查询字段作为分片键,例如用户ID,确保大多数查询落在同一分片;对跨分片操作采用汇总表或Elasticsearch二次聚合;全局ID使用雪花算法保持趋势递增;分页通过游标(last_id)避免offset;数据迁移采用双写模式,在低峰期验证一致性后切流。” 此话术可迁移至任何分库分表类项目 |
💡 面试官侧重点:大厂(字节、阿里、腾讯)格外关注分布式场景下的分片键选择与跨库查询优化,建议结合具体业务数据量(如亿级)阐述。Ruby技术栈项目中,常通过ShardingSphere-JDBC或MySQL Router实现透明分片。