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

一、"如何优化 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 命中率骤降(大量新数据读入,旧页被淘汰)
  • 网络抖动(但通常表现为超时而非单纯变慢)
相关推荐
李广坤18 小时前
MySQL 大表字段变更实践(改名 + 改类型 + 改长度)
数据库
爱可生开源社区2 天前
2026 年,优秀的 DBA 需要具备哪些素质?
数据库·人工智能·dba
随逸1772 天前
《从零搭建NestJS项目》
数据库·typescript
加号33 天前
windows系统下mysql多源数据库同步部署
数据库·windows·mysql
シ風箏3 天前
MySQL【部署 04】Docker部署 MySQL8.0.32 版本(网盘镜像及启动命令分享)
数据库·mysql·docker
李慕婉学姐3 天前
Springboot智慧社区系统设计与开发6n99s526(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
数据库·spring boot·后端
百锦再3 天前
Django实现接口token检测的实现方案
数据库·python·django·sqlite·flask·fastapi·pip
tryCbest3 天前
数据库SQL学习
数据库·sql
jnrjian3 天前
ORA-01017 查找机器名 用户名 以及library cache lock 参数含义
数据库·oracle
十月南城3 天前
数据湖技术对比——Iceberg、Hudi、Delta的表格格式与维护策略
大数据·数据库·数据仓库·hive·hadoop·spark