有哪些 SQL 优化方法?

考查重点:扫描、排序、回表与锁等待。以执行计划和耗时定位扫描、回表、排序或锁等待,按瓶颈调整索引、列数、批量与事务;在结果不变条件下同数据复测

本题目录

参考答案

核心回答

SQL 优化的核心是:在结果语义不变的前提下,减少扫描、传输、计算和等待的成本。 我会先结合执行计划与实际耗时找到主要开销,再选择对应的方法。以 MySQL 8.4 的常见业务查询为例:

  • 减少扫描和回表:围绕过滤、连接和排序需求设计索引,联合索引的列顺序要匹配查询;让条件尽量能在索引上形成有效定位范围,并评估覆盖查询的收益。
  • 减少读取和返回的数据:只查需要的列,限制合理的时间、业务范围和返回行数,避免为了几条结果读出大量明细后在应用里过滤。
  • 减少排序、聚合和连接的中间结果:尽早过滤无关数据,检查连接条件、结果放大和排序开销;业务不需要去重时才考虑 UNION ALL,不能为了快而改变结果。
  • 减少重复调用:把循环中的逐条查询改为合适的批量读取或关联查询;批量写入也要控制批次,避免把很多小往返换成一个不可控的大事务。
  • 减少等待:如果主要耗时来自锁等待、长事务或资源争用,应缩短事务、改进访问方式或处理竞争源,单改查询文本未必有效。

优化后还要验证结果与性能。命中了索引、消除了 filesort,或 SQL 写得更短,都不能单独证明它更快。

进一步解释

从查询方式出发设计索引和分页

以“查看某用户按创建时间排序的订单”为参考例子,可以评估 (user_id, created_at, id) 这样的索引,让用户过滤与时间顺序结合起来。是否需要再包含其他列,要权衡查询覆盖收益、索引体积和写入维护成本。

如果深分页主要消耗在跳过前面大量记录,可以在业务允许时改成游标分页:保留上一页最后的创建时间和唯一 ID,按相同排序继续查。时间可能重复,因此不能只用时间作为游标。它也不等价于任意跳到第几页,分页交互和一致性要求都要一起考虑。

若必须保留偏移分页,可以评估先定位窄索引中的主键,再获取所需明细,减少不必要的回表;但跳过大量索引项的工作并没有因此凭空消失。

查询改写首先要保持语义

按自然日筛选时间字段时,可以考虑直接表达清楚起止边界,避免为每一行重复计算日期;边界要与时区和字段含义一致。已有合适的函数索引时,也不能一概说“出现函数就不能使用索引”。

改写连接、子查询和聚合时,则要关注重复行、NULL、外连接及聚合前后的过滤含义。例如把外连接的条件从 ON 移到 WHERE,可能使原本要保留的行消失;COUNT(*) 与 COUNT(某列) 对空值的处理也不同。优化器本身还会做等价转换,因此 JOIN、IN、EXISTS 哪个更快,需要结合具体计划判断。

怎样判断改到了真正的瓶颈?

先定位值得优化的 SQL,结合调用频率与总资源消耗排序,而不只找单次最慢的一条。然后用普通 EXPLAIN 看估算的访问路径、扫描量、连接方式,以及排序、临时结果等信息。

如果估算与实际表现不一致,再看统计信息、参数取值和数据倾斜。MySQL 8.4 的 EXPLAIN ANALYZE 会实际执行语句,并给出运行行数、循环次数和耗时,需要按相应执行开销使用,不能当成只解析 SQL。

比较改动前后时,应保持数据规模、参数、并发和缓存条件尽量可比;既看响应时间,也看扫描量、CPU、I/O,以及新增索引对写入的影响。

常见追问

分析型大查询,加了索引仍然慢怎么办?

先看最终需要处理的数据量。如果过滤后仍有海量结果,继续加索引未必能消除读取、聚合、排序和传输这些结果的成本。

对于 Doris 这类分析引擎,可以从执行计划检查分区裁剪、连接顺序和数据分布,再用 Profile 看扫描、跨节点交换及各执行阶段的耗时。高频、口径稳定的分析可以评估预聚合或物化视图;海量明细出数也可能需要限定查询范围、分批或异步处理。预计算会带来存储、更新和数据新鲜度的取舍,不能直接套用 MySQL 点查索引的经验。

是不是应该尽量不用 JOIN、子查询、NULL 或 OR?

没有这样的通用规则。需要判断它们在当前引擎、数据和查询条件下产生了什么访问路径与中间结果。把一次合理的关联拆成大量应用层查询,反而可能增加往返和一致性问题。

参考资料

相关面经

7 篇

波克、比亚迪、客路、CVTE、石犀科技

面经合集
查看原面经 →
随着数据量持续增长,优化之后过一段时间又变慢怎么办?

字节跳动

AI 全栈开发工程师
校招秋招一面
查看原面经 →
第二段实习中, 针对营销场景模块, 你对其中的大规模数据的SQL是如何处理优化的?

公司未注明

面经合集
查看原面经 →
一个很长的sql语句如何优化 如果数据量很大呢?

阿里巴巴

后端开发
日常实习一面
查看原面经 →
慢SQL怎么排查和优化?

亚信科技

后端开发工程师(AI / Agent 方向)
查看原面经 →
9,优化sql的方法(就记得避免使用select*了 )
展开剩余 2 篇面经

高德

Agent 后端开发
日常实习一面
查看原面经 →
d. 怎么判断SQL是否慢,命中了索引没有?

转转

后端开发
实习一面
查看原面经 →
e. 线上碰见过 SQL 慢查询的情况吗?怎么处理?

相关题目

4 题