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

一、"如何优化 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 命中率骤降(大量新数据读入,旧页被淘汰)
  • 网络抖动(但通常表现为超时而非单纯变慢)
相关推荐
十二同学啊3 小时前
向量数据库:从核心原理到 RAG 与 Java 实战
数据库
蓝速科技4 小时前
会议室门牌公告通知发布选型与落地指南丨蓝速科技
大数据·运维·数据库·人工智能·科技
一 乐6 小时前
二手交易平台|基于springboot + vue二手交易平台(源码+数据库+文档)
java·数据库·vue.js·spring boot·小程序
SelectDB7 小时前
同等资源下 Apache Doris 4.2 vs StarRocks 4.1.1:1TB SSB 与 TPC-H 性能实测
大数据·数据库·数据分析
这个DBA有点耶7 小时前
数据库集群与分布式架构:三条技术路线对比、金仓KES RAC实测数据与决策框架
数据库·程序员·架构
MayBaymax7 小时前
MongoDB 索引与事务
java·数据库·mongodb
SelectDB7 小时前
Doris vs ClickHouse:企业 OLAP 走向下一阶段,两种技术路线如何选择
大数据·数据库·数据分析
vx-程序开发7 小时前
【计算机毕设】django校园跑腿服务系统83141
java·数据库·vue.js·spring boot·spring·elasticsearch·django
一 乐8 小时前
养老院管理系统|基于springboot + vue养老院管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·毕业设计
这个DBA有点耶9 小时前
InnoDB页结构深入:页分裂、页合并、填充因子——B+树底层机制全解析
数据库·mysql·架构