数据库性能调优中的经典高频面试题

一、"如何优化 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_sizemax_heap_table_size(但治标不治本)
    • 极端情况:将聚合逻辑移到应用层(如用 Redis HyperLogLog 做 UV 统计)

总结索引是根治方案,SQL 写法是前提,参数调优是兜底。


二、"百万级分页怎么优化?"

痛点LIMIT 1000000, 20 要跳过 100 万行,性能随偏移量线性下降。

面试回答:

"传统 LIMIT offset, size 在深分页场景下效率极低,因为 MySQL 必须扫描并丢弃前 offset 行。我的优化策略分三层:"

1. 游标分页(Cursor-based Pagination)------ 首选方案
  • 利用有序唯一字段(如自增 ID、时间戳)作为"游标",避免偏移。

  • 要求 :前端传回上一页最后一条记录的 idcreate_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 命中率骤降(大量新数据读入,旧页被淘汰)
  • 网络抖动(但通常表现为超时而非单纯变慢)
相关推荐
合方圆~小文2 分钟前
工业摄像头工作原理与核心特性
数据库·人工智能·模块测试
jmxwzy7 分钟前
Redis
数据库·redis·缓存
零叹10 分钟前
Redis热Key——大厂是怎么解决的
数据库·redis·缓存·热key
王五周八11 分钟前
基于 Redis+Redisson 实现分布式高可用编码生成器
数据库·redis·分布式
win x13 分钟前
Redis事务
数据库·redis·缓存
飞翔的小->子>弹->14 分钟前
CMK、CEK
服务器·数据库·oracle
peixiuhui20 分钟前
Iotgateway技术手册-7. 数据库设计
数据库·iotgateway·开源dotnet·arm工控板·开源网关软件·开源数据采集
麦兜*26 分钟前
【Spring Boot】 接口性能优化“十板斧”:从数据库连接到 JVM 调优的全链路提升
java·大数据·数据库·spring boot·后端·spring cloud·性能优化
qq_3344668633 分钟前
U9补丁同步的时候报错
数据库
施嘉伟36 分钟前
KSQL Developer 测试记录
数据库·kingbase