发现并修复慢查询
针对“这个查询很慢,修复它”面试题的诊断清单。
发现并修复慢查询 是 CoddyKit 上的免费 SQL Interview Prep 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 SQL Interview Prep 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 SQL Interview Prep 课程共包含 4 节课。
“这个查询很慢,请修复它”提示
这是压轴面试题:面试官会给您一条慢查询和一份 EXPLAIN ANALYZE 执行计划,要求您诊断问题。他们考察的是一种方法,而不是死记硬背的技巧。
高质量的回答会按照清单逐项说明:测量、阅读执行计划、找出主要成本、提出假设、给出修复方案,然后进行验证。本课会逐步构建这份清单。
请保持系统性并讲述您的推理过程,这才是获得高级职位评价的关键。
第 1 步:使用 EXPLAIN ANALYZE 进行测量
不要仅凭查询语句猜测。请使用 EXPLAIN (ANALYZE, BUFFERS) 获取真实的执行计划。
ANALYZE 会提供实际耗时和行数;BUFFERS 会显示数据是在缓存中命中,还是需要从磁盘读取。两者结合起来,就能告诉您查询是受 CPU 限制、受输入输出限制,还是只是执行了过多工作。
请运行几次;第一次运行可能会受到冷缓存开销的影响,从而使耗时失真。
EXPLAIN (ANALYZE, BUFFERS)
SELECT * FROM orders o
JOIN customers c ON c.id = o.customer_id
WHERE o.created_at >= '2026-01-01';第 2 步:找出主要节点
不要从上到下随意查找。请找到实际花费最多时间的节点。
计算每个节点的自身耗时:用它的总 actual time 减去子节点的耗时,再乘以 loops。占比最大的节点就是您的目标;其他内容都只是噪声。
在面试中可以这样说:80% 的运行时间都花在这个顺序扫描上,因此我会从这里入手。 优化其他部分只会浪费精力。
第 3 步:检查估算值与实际值
在主要节点处,将估算行数与实际行数进行比较。两者差距很大,说明规划器缺乏可靠依据,很可能选择了错误的执行计划(错误的连接算法或错误的访问方式)。
示例中的低估达到了 1000 倍。在重新设计任何内容之前,请先更新统计信息;这条命令通常就能免费修正执行计划。
ANALYZE 会重新计算列统计信息;VACUUM ANALYZE 还会清理死元组并更新可见性映射。
-- estimate rows=100, actual rows=120000 -> stale stats
ANALYZE orders;
-- or, for bloated tables:
VACUUM ANALYZE orders;常见原因:对已建立索引的列使用函数
最常见且可以修复的错误是:函数或类型转换包裹了 WHERE 条件中的列,因此无法使用索引,数据库引擎只能执行顺序扫描。
示例中,DATE() 会应用于每一行,因此强制进行了完整扫描。请将其改写为直接使用列的范围谓词(可利用索引的形式),created_at 上的索引就会生效。
WHERE lower(email)=... 也是同样的道理:您可以存储标准化数据,查询原始列,或者建立表达式索引。
-- Not sargable: index unusable
WHERE DATE(created_at) = '2026-01-01'
-- Sargable: range over the bare column
WHERE created_at >= '2026-01-01'
AND created_at < '2026-01-02'常见原因:缺少索引
如果主要节点是带有高选择性过滤条件的顺序扫描,或者是内侧连接键没有索引且 loops 巨大的嵌套循环,解决方案通常是建立索引。
请在用于过滤或连接的列上建立索引。示例在 customer_id 上创建索引,使连接可以从顺序扫描切换为索引扫描,规划器也可能因此选择成本低得多的执行计划。
请重新运行 EXPLAIN ANALYZE 进行验证,不要想当然地认为索引一定有帮助。
CREATE INDEX idx_orders_customer
ON orders (customer_id);常见原因:使用 SELECT * 和宽行
SELECT * 会从磁盘读取每一列并通过网络传输,而且会阻止仅索引扫描,因为索引很少能覆盖所有列。
请只选择所需的列。这样可以缩小行宽、降低输入输出开销,并可能启用覆盖索引的仅索引扫描。
面试官故意放入 SELECT *,就是希望您注意到它。在宽表上,精简列列表通常能带来快速而实际的收益。
-- Before
SELECT * FROM orders WHERE customer_id = 42;
-- After: only needed columns (may enable index-only scan)
SELECT order_id, amount FROM orders WHERE customer_id = 42;常见原因:溢写到磁盘
如果 Sort 或 Hash 节点报告了磁盘使用情况(Sort Method: external merge Disk: 25000kB 或 Batches: > 1),说明该操作超出了 work_mem,并溢写到了磁盘。
可选方案包括:为当前会话调大 work_mem,减少到达排序或哈希操作的行数(提前过滤),或者建立能够提供排序顺序的索引,从根本上避免排序。
这是面试官会认可的精确的高级水平诊断。
Sort (actual rows=2000000 loops=1)
Sort Key: o.amount
Sort Method: external merge Disk: 25000kB常见原因:获取过多行
请留意 Rows Removed by Filter: 9500000。查询读取了 1000 万行,却丢弃了几乎全部数据,这是典型的无效工作。
解决方法包括:建立索引,让过滤在访问数据时而不是访问之后执行;让谓词具有更高的选择性;或者在查询中更早地进行过滤,使更少的行沿着执行树向上传递。
原则是:尽可能少做工作,尽早并以尽可能低的成本进行过滤。
Seq Scan on events
Filter: (event_type = 'purchase')
Rows Removed by Filter: 9500000诊断清单
在面试中背出这份清单,您就不会偏离方向:
- 测量:使用
EXPLAIN (ANALYZE, BUFFERS)。 - 定位:找到消耗时间最多的节点。
- 比较:比较估算行数与实际行数,先修正过时的统计信息。
- 检查谓词是否可利用索引:从过滤列中移除函数。
- 建立索引:为选择性高的过滤条件和连接键建立索引。
- 精简:减少列,避免使用
SELECT *。 - 留意:磁盘溢写和获取过多数据。
- 验证:重新运行执行计划进行确认。
综合运用
请完整地口述一个示例。计划显示对一个包含 5,000 万行的 orders 表执行顺序扫描,筛选条件为 customer_id = 42,Rows Removed by Filter 接近 5,000 万,估算值与实际值大致吻合。
诊断:筛选条件具有选择性,没有索引,主要成本来自扫描。修复:CREATE INDEX ON orders(customer_id)。重新运行:计划切换为索引扫描,耗时从几秒降至不到一毫秒。
这个“测量—诊断—修复—验证”循环,就是回答任何慢查询问题时的答题模板。
CREATE INDEX idx_orders_customer ON orders (customer_id);
EXPLAIN (ANALYZE, BUFFERS)
SELECT order_id, amount FROM orders WHERE customer_id = 42;快速检查
某个查询使用 WHERE YEAR(order_date) = 2026 进行筛选,尽管 order_date 上已有 B 树索引,计划仍显示完整的顺序扫描。最好的第一步修复是什么?
回顾
现在,您已经掌握了一套可重复使用的方法来处理慢查询问题:
- 始终使用
EXPLAIN (ANALYZE, BUFFERS)进行测量,并关注主要节点。 - 当估算值与实际值出现偏差时,先修复过时的统计信息。
- 让谓词可利用索引,为高选择性的筛选条件和连接键添加索引,并精简
SELECT *。 - 处理磁盘溢写和过度获取数据,然后验证新的执行计划。
讲述检查清单,提出具体变更,并重新运行计划来证明效果,这才是资深工程师的回答。
用 AI 导师学习 SQL — 免费
在浏览器中编写并运行真实代码,获得全天候 AI 导师的即时帮助,并在网页或应用中继续学习。
- 课程
- 30
- 课程
- 120
常见问题解答
「发现并修复慢查询」课时是免费的吗?
是的 — 「发现并修复慢查询」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 SQL Interview Prep 课程的其余内容,请升级到 CoddyKit PRO。 SQL Interview Prep 课程共包含 4 节课。
「发现并修复慢查询」这节课中我会学到什么?
针对“这个查询很慢,修复它”面试题的诊断清单。 你通过在浏览器中直接运行的动手代码来练习 SQL Interview Prep,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 SQL Interview Prep 需要有经验吗?
无需任何先前经验。CoddyKit 上的 SQL Interview Prep 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。
「发现并修复慢查询」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 SQL Interview Prep 课中编写并运行代码吗?
能。每节 SQL Interview Prep 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。