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

一、"如何优化 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 命中率骤降(大量新数据读入,旧页被淘汰)
  • 网络抖动(但通常表现为超时而非单纯变慢)
相关推荐
VX:Fegn08956 分钟前
计算机毕业设计|基于ssm + vue超市管理系统(源码+数据库+文档)
前端·数据库·vue.js·spring boot·后端·课程设计
chipsense9 分钟前
电流传感器型号从数据库查询并排序输出到网页的方法
数据库·php·传感器·霍尔电流传感器
踢足球092912 分钟前
寒假打卡:2026-01-28
数据库·oracle
麦聪聊数据13 分钟前
智慧医疗数据互联互通:使用 QuickAPI 构建实时诊疗数据交换层
数据库·sql·安全
风吹落叶花飘荡22 分钟前
2026年mysql数据库迁移(全流程)
数据库·mysql
2301_8223827633 分钟前
Python上下文管理器(with语句)的原理与实践
jvm·数据库·python
m0_7482299934 分钟前
Laravel8.X核心功能全解析
开发语言·数据库·php
液态不合群1 小时前
【面试题】MySQL 的索引下推是什么?
数据库·mysql
2301_790300961 小时前
Python深度学习入门:TensorFlow 2.0/Keras实战
jvm·数据库·python
Code blocks2 小时前
SpringBoot从0-1集成KingBase数据库
数据库