sql语句调优教程

一、 优化总览与诊断流程

SQL 优化的本质不是盲目加索引,而是减少不必要的数据扫描、计算、返回量和等待。建议遵循"先找慢 SQL → 看执行计划 → 按优先级修复"的流程:

  1. 定位慢 SQL :通过数据库的慢查询日志或 pg_stat_statements(PostgreSQL)、Query Store(SQL Server)等工具找出耗时或执行频次最高的语句。

  2. 分析执行计划 :使用 EXPLAIN(MySQL)或 EXPLAIN ANALYZE(PostgreSQL)查看数据库打算如何执行查询。

  3. 识别瓶颈:重点关注全表扫描(Seq Scan / ALL)、高预估扫描行数(rows)、临时表(Using temporary)和文件排序(Using filesort)。

  4. 按优先级修复:优先调整索引和 SQL 写法,其次考虑业务逻辑拆分,最后才进行数据库参数调优。

二、 索引设计与防失效

索引是提升查询性能最有效的手段,但用错反而会拖慢写入。

  • 建索引原则 :优先考虑在 WHEREJOINORDER BYGROUP BY中高频出现的字段建索引。组合索引需遵循最左前缀原则,并将区分度高、等值条件的字段放在前面。

  • 覆盖索引 :如果查询的字段全部包含在索引中,数据库无需回表查原数据,性能极高。例如为 SELECT id, name FROM user WHERE email = 'xx@xx.com'建立 (email, id, name)索引。

  • 避免索引失效 :对索引列使用函数(如 DATE(create_time) = '2024-01-01')、表达式计算(如 age + 1 = 30)或前置模糊匹配(如 LIKE '%abc')都会导致索引失效。应改写为范围查询或后置模糊匹配。

三、 SQL 语句改写技巧

良好的 SQL 写法能从源头减少数据库负担:

  • 拒绝 SELECT *:只取需要的字段,既能减少网络传输和内存占用,也能更好地配合覆盖索引。

  • 提前过滤数据 :尽早使用 WHERE缩小数据集,再进行 JOIN或聚合操作。

  • 优化子查询与判断 :能用 JOIN就少用子查询;在大结果集判断存在性时,用 EXISTS替代 IN;不需要去重时坚决不用 DISTINCT

  • 分页优化 :深分页(如 LIMIT 100000, 10)性能极差。应改用游标分页,如 WHERE id > 100000 LIMIT 10,利用索引直接定位。

  • 慎用 OR与隐式转换 :不同列上的 OR容易导致索引失效;字段类型不匹配会引发隐式转换,同样会让索引失效。

四、 复杂场景与高级策略

当单表数据量极大或查询极其复杂时,可采用以下进阶方案:

  • 大表 JOIN 与聚合:确保关联字段有索引;聚合查询可借助覆盖索引或预先计算的汇总表。

  • 窗口函数 :处理排名、环比等分析类需求时,使用窗口函数(如 ROW_NUMBER()LAG())替代耗时的自连接。

  • 表分区:针对亿级大表,可按时间或哈希进行分区,查询时数据库会自动裁剪无关分区,减少扫描。

  • 物化视图与预计算:对高频复杂的报表查询,使用物化视图或定时任务预计算结果,用空间换时间。

  • 批量操作:大批量写入或更新时,采用分批提交(Batch),避免长事务导致锁等待和资源耗尽。

五、 实战演练与避坑指南

案例 1:单表查询慢

问题 SQLSELECT * FROM orders WHERE DATE(create_time) = '2024-01-01';

分析与优化DATE()函数导致 create_time索引失效,引发全表扫描。

改写SELECT id, amount FROM orders WHERE create_time >= '2024-01-01' AND create_time < '2024-01-02';

配套动作 :建立覆盖索引 idx_create_time_covering (create_time, id, amount)

案例 2:深分页卡顿

问题 SQLSELECT id, name FROM user ORDER BY id LIMIT 100000, 10;

分析与优化:数据库需先读取前 100010 行再丢弃,越往后越慢。

改写SELECT id, name FROM user WHERE id > 100000 ORDER BY id LIMIT 10;

案例 3:大结果集 IN 子查询

问题 SQLSELECT * FROM customers WHERE customer_id IN (SELECT customer_id FROM orders WHERE ...);

分析与优化 :大结果集下 IN性能不佳。

改写 :使用 EXISTS或改写为 JOIN

避坑清单

  1. 过度索引 :索引虽快但占空间,会降低 INSERT/UPDATE/DELETE的速度,单表索引不宜过多。

  2. 低区分度字段:在"性别"这类只有几个值的列上建索引毫无意义。

  3. 陈旧统计信息:数据库优化器依赖统计信息生成最优计划,需定期更新表统计信息。

  4. 锁竞争:大事务和未提交的长连接会导致严重的锁等待,应避免在事务中做无关耗时操作