一、"如何优化 GROUP BY 导致的临时表?"
核心问题 :
Using temporary表示 MySQL 创建了临时表(可能落盘),性能极差。
面试回答:
"当
EXPLAIN显示Extra: Using temporary时,说明 MySQL 无法利用索引完成分组,必须借助临时表。我会从以下三方面优化:"
1. 确保 GROUP BY 字段有合适索引(最有效!)
-
InnoDB 中,如果
GROUP BY的字段有序且连续,MySQL 可以直接在索引上完成分组,无需临时表。 -
最佳实践 :为
GROUP BY字段建立前缀索引 ,且与WHERE条件组成联合索引。-- 慢查询:无索引,建临时表 + filesort
SELECT user_id, COUNT(*) FROM orders WHERE create_time > '2025-01-01' GROUP BY user_id;-- 优化:建 (create_time, user_id) 索引
ALTER TABLE orders ADD INDEX idx_ctime_uid (create_time, user_id);
效果:Extra 变为 Using index,无临时表、无排序。
2. 避免 SELECT 非聚合字段(违反 ONLY_FULL_GROUP_BY)
- 如果
sql_mode包含ONLY_FULL_GROUP_BY(MySQL 5.7+ 默认开启),写SELECT name, COUNT(*) FROM t GROUP BY id会报错。 - 即使关闭了,MySQL 仍可能因不确定取哪一行而建临时表。
✅ 正确写法:
-- 要么只查 GROUP BY 字段 + 聚合函数
SELECT user_id, COUNT(*), MAX(create_time) FROM orders GROUP BY user_id;
-- 要么用窗口函数(MySQL 8.0+)
SELECT DISTINCT user_id, COUNT(*) OVER(PARTITION BY user_id) FROM orders;
3. 减少结果集大小 & 控制内存使用
- 如果分组后数据量巨大,即使有索引也可能因
tmp_table_size不足而写磁盘。 - 对策 :
- 先加
WHERE过滤数据(如限定时间范围) - 调大
tmp_table_size和max_heap_table_size(但治标不治本) - 极端情况:将聚合逻辑移到应用层(如用 Redis HyperLogLog 做 UV 统计)
- 先加
总结 :索引是根治方案,SQL 写法是前提,参数调优是兜底。
二、"百万级分页怎么优化?"
痛点 :
LIMIT 1000000, 20要跳过 100 万行,性能随偏移量线性下降。
面试回答:
"传统
LIMIT offset, size在深分页场景下效率极低,因为 MySQL 必须扫描并丢弃前offset行。我的优化策略分三层:"
1. 游标分页(Cursor-based Pagination)------ 首选方案
-
利用有序唯一字段(如自增 ID、时间戳)作为"游标",避免偏移。
-
要求 :前端传回上一页最后一条记录的
id或create_time。-- 第一页
SELECT * FROM messages ORDER BY id LIMIT 20;-- 第二页(前端传 last_id = 10020)
SELECT * FROM messages WHERE id > 10020 ORDER BY id LIMIT 20;
优势:始终走索引,时间复杂度 O(log N + size),与总数据量无关。
⚠️ 注意:若按非唯一字段(如
create_time)分页,需加主键防重复:
WHERE (create_time, id) > ('2025-01-01 12:00:00', 10020)
2. 延迟关联(Deferred Join)------ 兼容传统分页
-
先通过覆盖索引查出主键,再回表取数据,减少回表次数。
-- 传统(慢):回表 100 万次
SELECT * FROM messages ORDER BY id LIMIT 1000000, 20;-- 优化(快):只回表 20 次
SELECT m.* FROM messages m
INNER JOIN (
SELECT id FROM messages ORDER BY id LIMIT 1000000, 20
) tmp ON m.id = tmp.id;
适用场景:无法改前端接口时的折中方案。
3. 业务层限制 + 异步预加载
- 限制最大翻页深度(如只允许查前 100 页),超过则提示"请用搜索"。
- 对高频访问页(如第 1~10 页)做 Redis 缓存。
- 超大数据量报表:改用 导出任务(异步生成 CSV)。
总结 :能改接口就用游标分页;不能改就用延迟关联;实在不行就限制或异步化。
三、"一条 SQL 突然变慢,可能是什么原因?"
关键点 :强调"突然变慢" ≠ 一直慢,说明环境或数据发生了变化。
面试回答:
"SQL 突然变慢通常是执行计划突变 或外部干扰导致。我会按以下优先级排查:"
🔍 1. 统计信息过期 → 优化器选错执行计划(最常见!)
-
表数据分布发生剧烈变化(如某状态从 1% 变成 90%),但统计信息未更新。
-
优化器误判成本,选择了全表扫描而非索引。
-
解决 :
ANALYZE TABLE your_table; -- 更新统计信息
2. 隐式参数变化
- 应用传参类型变了(如
user_id = '123'变成字符串,而字段是 INT) - 时间范围扩大(如
WHERE date > '2020-01-01'→> '2010-01-01'),导致返回数据量暴增
3. 锁竞争或长事务阻塞
-
其他会话持有行锁/间隙锁,当前 SQL 被阻塞。
-
排查 :
SHOW PROCESSLIST; -- 看是否有 State=Locked SHOW ENGINE INNODB STATUS\G -- 查看 LATEST DETECTED DEADLOCK / TRANSACTIONS
4. 系统资源瓶颈
- 磁盘 I/O 打满(其他大查询或备份任务)
- Buffer Pool 命中率骤降(大量新数据读入,旧页被淘汰)
- 网络抖动(但通常表现为超时而非单纯变慢)