面试官最爱问什么?除了JVM和并发,MySQL优化绝对是出场率前三的考点。尤其2026年,大厂对数据库功底的要求越来越高——不仅要会写SQL,更要能说清为什么慢、怎么优化、原理是什么。
这10道题是从近半年面经中提炼的高频题,题题都有标准答案。
1. 一条SQL执行很慢,怎么排查?
不直接答”加索引”,应该展示排查思路:
-- 第一步:开慢查询日志
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 1;
-- 第二步:用EXPLAIN看执行计划
EXPLAIN SELECT * FROM orders WHERE user_id = 100 AND status = 'paid';
关键看 type(ALL最差→index→range→ref→const最好)、rows(扫描行数)、Extra(Using filesort/Using temporary都是坏信号)。然后用 SHOW PROFILE 看各阶段耗时,定位是IO瓶颈还是CPU瓶颈。
2. EXPLAIN结果里的type字段,哪些需要优化?
| type | 含义 | 是否要优化 |
|---|---|---|
| ALL | 全表扫描 | ✅ 必须优化 |
| index | 全索引扫描 | ⚠️ 数据量大时需优化 |
| range | 索引范围扫描 | 一般OK |
| ref | 非唯一索引查找 | ✅ 理想 |
| eq_ref | 唯一索引查找 | ✅ 最优 |
| const | 主键等值 | ✅ 完美 |
面试加分点: 提到 index 虽然比 ALL 好,但如果 Extra 列显示 Using index(覆盖索引),那就是最优情况——连回表都省了。
3. 索引建得越多越好吗?
恰恰相反。 三个代价:
- 写入性能下降:每插入/更新一行,所有索引都要维护,索引越多写入越慢
- 占用磁盘空间:索引本身是数据结构,一个表的索引可能比数据还大
- 优化器选择困难:索引太多,MySQL优化器可能选错索引
实践建议: 单表索引控制在5个以内。遵循”最左前缀”原则让一个联合索引覆盖多个查询场景。
4. 什么是覆盖索引?什么时候用?
覆盖索引是指查询的所有字段都在索引中,不需要回表查聚簇索引。
-- 建联合索引
CREATE INDEX idx_user_status_time ON orders(user_id, status, create_time);
-- 这个查询就是覆盖索引:SELECT的字段全在索引里
SELECT user_id, status, create_time
FROM orders
WHERE user_id = 100 AND status = 'paid';
EXPLAIN的Extra列会显示 Using index——不需要回表,性能最高。联合索引的列顺序很关键,必须把等值查询列放前面,范围查询列放后面。
5. 分库分表怎么选sharding key?
核心原则:让大多数查询落到单个分片上。
// 如果是电商订单表,user_id做sharding key
// 因为查"我的订单"是最高频场景
int shardId = userId.hashCode() % shardCount;
避免跨分片查询是关键。如果按 order_id 分片,查”我的订单”就要扫所有分片再聚合——这就是选错sharding key的典型后果。
面试加分: 提一嘴基因法——把用户ID编码进订单ID(如 order_id = userId后6位 + 时间戳 + 序列号),这样按订单查也能定位分片。
6. MySQL主从延迟怎么解决?
主从延迟本质是从库回放binlog的速度跟不上主库写入速度。
排查三步走:
-- 查看延迟秒数
SHOW SLAVE STATUS\G -- 看 Seconds_Behind_Master
解决方案(按严重程度递进): 1. 并行复制:MySQL 5.7+ 开启 slave_parallel_workers=4,按库并行回放 2. 半同步复制:rpl_semi_sync_master_wait_for_slave_count=1,主库等至少1个从库确认 3. 拆分大事务:一个事务更新100万行→拆成10个10万行的事务 4. 读写分离兜底:关键业务强制读主库,非关键业务读从库
7. count(*) 和 count(1) 和 count(id) 哪个快?
答案是:几乎一样快。 在InnoDB中,count(*) 会选最小的二级索引来扫(因为二级索引比聚簇索引小),count(1) 和 count(id) 同理。MySQL 8.0优化器对 count(*) 做了专门优化,实际上 count(*) 就是最优写法。
-- 三者性能相同,但count(*)语义最清晰,推荐
SELECT count(*) FROM orders WHERE status = 'paid';
真正影响性能的是WHERE条件有没有索引。没有索引,三种写法都是全表扫描。
8. 什么情况下索引会失效?
这道题面试官想听的不仅是列举,更要说明为什么失效:
- LIKE以%开头 → 索引有序(B+树),前缀模糊无法定位起点
- 联合索引不满足最左前缀 →
(a,b,c)索引,查b=1用不上 - WHERE条件中对索引列做函数/运算 →
WHERE YEAR(create_time)=2026,优化器无法用索引 - 类型隐式转换 →
WHERE phone=13800138000,phone是varchar,MySQL把字符串转数字,索引失效 - OR条件中有一个列没索引 →
WHERE a=1 OR b=2,b没索引→全表扫描
终极解决方案: OR改成UNION:
-- ❌ OR可能导致索引失效
SELECT * FROM t WHERE a = 1 OR b = 2;
-- ✅ UNION保证各自走索引
SELECT * FROM t WHERE a = 1 UNION SELECT * FROM t WHERE b = 2;
9. 大表加索引为什么不阻塞读写?Online DDL原理
MySQL 5.6+ 支持 Online DDL,加索引时不锁表。原理:
- 建立一个临时文件,记录DDL期间的数据变更(row log)
- 扫描原表数据,构建新索引
- 应用row log中的变更到新索引
- 原子切换新旧索引
-- ALGORITHM=INPLACE 不拷贝全表,LOCK=NONE 不锁表
ALTER TABLE orders ADD INDEX idx_status(status) ALGORITHM=INPLACE, LOCK=NONE;
⚠️ 坑: Online DDL需要额外的磁盘空间(约等于表大小),空间不足会失败。生产环境执行前先 df -h 确认空间。
10. 如何优化分页查询,特别是深分页?
-- ❌ 这种深分页:MySQL需要扫描前100010行,丢掉前100000行
SELECT * FROM orders ORDER BY id LIMIT 100000, 10;
方案一(最推荐):延迟关联
SELECT * FROM orders
INNER JOIN (SELECT id FROM orders ORDER BY id LIMIT 100000, 10) AS tmp
ON orders.id = tmp.id;
子查询只扫索引不回表,拿到10个id后再回表取完整数据。
方案二:游标分页
-- 记住上一页最后一条的id,下一页从这开始
SELECT * FROM orders WHERE id > 100000 ORDER BY id LIMIT 10;
面试加分点: 方案二不支持跳页,但ES和Twitter的Timeline都是这么干的——深分页本身就是个伪需求,用户不会翻到第1000页。
这10道题覆盖了索引、SQL优化、架构设计三个维度。面试时别只背答案,要在回答里展示排查思路——面试官最喜欢听”先看慢查询日志→EXPLAIN分析→看索引使用情况→考虑SQL改写”这种结构化思考。
如果这篇文章对你有帮助,转发给也在准备面试的朋友。
转自:云栖码客,作者:Java栈
版权申明:内容来源网络,版权归原创者所有,如有侵权请联系删除
想了解更多干货,可通过下方扫码关注

可扫码添加上智启元官方客服微信👇

17认证网








