慢查询优化
将以下提示词粘贴到你的 AI 对话框中:
请根据 https://skillhub.cn/install/skillhub.md,安装 @user_3c6cb52e/query-optimization。
技能介绍
要解决什么问题
数据库查询变慢时,工程师通常面临两类困境:一是慢查询日志里的 SQL 看起来无法改进,二是ORM 层在生产环境中突然触发大量小查询。一个原本 200ms 的页面响应,可能因为一次全表扫描或 N+1 查询膨胀到数秒,甚至拖垮整个实例。这个技能要解决的正是这类可测量的数据库性能退化,而不是泛泛的“数据库优化”。
它如何工作
技能围绕一套可验证的流程展开:
- 定位慢查询:从慢查询日志、APM 或用户反馈中收集目标 SQL,记录执行耗时、表规模与调用频率。高频慢查询优先级高于偶发查询。
- 分析执行计划:对 PostgreSQL 使用
EXPLAIN ANALYZE,对 MySQL 使用EXPLAIN FORMAT=JSON。重点关注全表扫描、高估算行的嵌套循环连接、未索引列排序,以及估算行数与实际行数之间的差异。 - 识别优化机会:为
WHERE、JOIN、ORDER BY涉及的列添加索引;将子查询改写为 JOIN;用具体列替换SELECT *;在合适位置添加LIMIT;使用覆盖索引避免回表;消除冗余条件。 - 逐项应用优化:每次只改动一处,以便独立衡量效果。对于 N+1 问题,将懒加载改为预加载(如 Django 的
select_related/prefetch_related、Prisma 的include、SQLAlchemy 的joinedload)。 - 测量与验证:对优化后的查询再次运行
EXPLAIN ANALYZE,对比执行时间、扫描行数与计划结构,确认结果一致,并检查新索引是否显著影响写入性能。 - 配置持续监控:在 PostgreSQL 中启用
log_min_duration_statement(如 100ms 阈值),结合pg_stat_statements、Datadog、Grafana 等工具追踪查询趋势。
支持 PostgreSQL、MySQL 以及 SQLAlchemy、Django ORM、Prisma、ActiveRecord、Sequelize、TypeORM 等常见 ORM,并可对接 pganalyze、Datadog APM、New Relic、Grafana + Prometheus 等监控体系。
适用边界与注意点
- 不要只用
EXPLAIN:缺少ANALYZE的执行计划只提供估算值,实际行数与耗时可能偏差很大。 - 复合索引要匹配查询模式:例如
(status, ordered_at)的复合索引通常优于两列各自建索引,因为可以走一次索引范围扫描。 - 避免
SELECT *:宽行增加 I/O,也阻碍覆盖索引生效。 - 关注统计信息偏移:大批量导入后,查询规划器可能仍使用过期统计信息,需要运行
ANALYZE或ANALYZE TABLE刷新。 - 警惕索引膨胀:高写入表上的频繁更新和删除会使索引体积膨胀,定期
REINDEX或OPTIMIZE TABLE。 - ORM 生成的 SQL 不总是最优:有时 ORM 会生成不必要的
LEFT JOIN或子查询,可用 Django 的QuerySet.query或 Knex 的.toSQL()检查实际 SQL,并在必要时覆盖。
使用场景
- 应用上线后订单列表接口响应从 200ms 涨到 4 秒,需要用 EXPLAIN ANALYZE 定位全表扫描并补建复合索引
- Django 后台加载 100 条订单触发 201 条 SQL,需要用 select_related 与 prefetch_related 消除 N+1 查询
- 报表查询在百万行订单表上耗时数秒,需要改写 SQL、去掉 SELECT * 并用覆盖索引加速
- 生产环境高频更新删除使查询越来越慢,需要诊断索引膨胀并安排 REINDEX 或 OPTIMIZE TABLE
适合人员
- 后端工程师:订单接口频繁触发慢查询告警,需要用 EXPLAIN ANALYZE 定位瓶颈并验证优化前后耗时
- ORM 应用开发者:Django 或 SQLAlchemy 页面加载触发大量小查询,需要定位 N+1 模式并改为预加载
- DBA:负责高写入表的索引健康,需要监控统计信息偏移与索引膨胀并安排定期维护
- 数据工程师:负责报表查询性能,需要在大数据量下改写 SQL 并用执行计划对比验证效果