写点什么

如何医治一条慢 SQL?

  • 2025-05-15
    福建
  • 本文字数:2276 字

    阅读完需:约 7 分钟

1 术前检查:找准病灶


1.1 EXPLAIN 查看执行计划


使用 EXPLAIN 查看 SQL 语句的执行计划,相当于给 SQL 拍了张 X 光。


下面是一个典型的 SQL 问题,它是某电商平台历史订单查询的 SQL 语句:

SELECT * FROM orders o LEFT JOIN users u ON o.user_id = u.idLEFT JOIN products p ON o.product_id = p.idWHERE o.create_time > '2023-01-01'  AND u.vip_level > 3  AND p.category_id IN (5,8)ORDER BY o.amount DESC LIMIT 1000,20;
复制代码


使用 EXPLAIN 关键字查看执行计划的结果如下:

+----+-------------+-------+------+---------------+------+---------+------+---------+---------------------------------+| id | select_type | table | type | possible_keys | key  | rows    | Extra| key_len | +----+-------------+-------+------+---------------+------+---------+------+---------+---------------------------------+| 1  | SIMPLE      | o     | ALL  | idx_user_time | NULL | 1987400 | Using where; Using filesort     |  | 1  | SIMPLE      | u     | ALL  | PRIMARY       | NULL | 100000  | Using where                     || 1  | SIMPLE      | p     | ALL  | PRIMARY       | NULL | 50000   | Using where                     |+----+-------------+-------+------+---------------+------+---------+------+---------+---------------------------------+
复制代码


诊断报告

  • 全表扫描三连击(type=ALL)

  • filesort 暴力排序(内存警告)

  • 索引全军覆没


2 手术方案:精准打击


2.1 单表代谢手术


如果通过执行计划查到是索引有问题,我们就需要单独优化索引。


病根:JSON 字段索引失效


错误用法:

ALTER TABLE users ADD INDEX idx_extend ((extend_info->'$.is_vip')); 
复制代码


extend_info 字段是 JSON 类型的字段,即使创建了索引,索引也会丢失。

正解姿势(MySQL 8.0+):

ALTER TABLE users ADD INDEX idx_vip_level (vip_level);ALTER TABLE orders ADD INDEX idx_create_user (create_time, user_id) COMMENT '组合索引覆盖查询';
复制代码


创建组合索引覆盖查询。


2.2 血管疏通术


卡点分析


原始 join 顺序是:

orders → users → products
复制代码


优化后的方案:

(子查询过滤users) → products → orders
复制代码


调整执行顺序,用小表驱动大表。


重写后的 SQL:

SELECT o.* FROM products p INNER JOIN (  SELECT o.id, o.amount, o.create_time   FROM orders o   WHERE o.create_time > '2023-01-01') o ON p.id = o.product_id INNER JOIN (  SELECT id   FROM users   WHERE vip_level > 3) u ON o.user_id = u.id  WHERE p.category_id IN (5,8)ORDER BY o.amount DESC LIMIT 1000,20;
复制代码


术后效果

  • 先扫小表(users 过滤后只有 100 条)

  • 消除冗余字段传输

  • 减少 Join 时临时表生成


2.3 开颅手术


通过执行计划锁定了问题,走错索引了,该怎么处理呢?


可以通过 FORCE INDEX 强制指定索引:

SELECT /*+ INDEX(o idx_create_user) */        o.id, o.amount FROM orders o FORCE INDEX (idx_create_user)WHERE o.create_time > '2023-01-01';
复制代码


使用衍生表加速:

SELECT *FROM (  SELECT id, amount   FROM orders   WHERE create_time > '2023-01-01'  ORDER BY amount DESC   LIMIT 1020 ) tmp ORDER BY amount DESC LIMIT 1000,20;
复制代码


医嘱

  • 警惕 OR 导致的索引失效

  • 用覆盖索引避免回表查询

  • CTE 表达式谨慎使用


2.4 生命体征监测


查看索引使用:

SHOW INDEX FROM orders;
复制代码


监控索引使用率:

SELECT object_schema, object_name, index_name,       count_read, count_fetch FROM performance_schema.table_io_waits_summary_by_index_usageWHERE index_name IS NOT NULL;
复制代码


3 术后护理:体系化治理


3.1 SQL 消毒中心


需要制定优秀的代码规范,否则可能会出现全表扫描的问题。

在日常工作中,我们要尽可能减少 Java 代码感染源

MyBatis 危险写法:

@Select("SELECT * FROM orders WHERE #{condition}")List<Order> findByCondition(@Param("condition") String condition);
复制代码


condition 参数可以传入任何内容,如何传入了 1=1,可能会导致查询所有的数据,走全表扫描,让查询效率变得非常低。

正确做法(参数化查询):

@Select("SELECT * FROM orders WHERE create_time > #{time}")List<Order> findByTime(@Param("time") Date time);
复制代码


消毒方案

  1. SQL 审核平台接入(如 Yearning)

  2. MyBatis 拦截器拦截全表更新

  3. 自动化 EXPLAIN 分析流水线


3.2 查杀大表癌症


如果遇到大表的癌症病例,可以用分库分表的方案解决。

病历案例:3 亿订单表终极解决方案

// Sharding-JDBC分片配置spring.shardingsphere.rules.sharding.tables.orders.actual-data-nodes=ds$0..1.orders_$->{2020..2023}spring.shardingsphere.rules.sharding.tables.orders.table-strategy.standard.sharding-column=create_timespring.shardingsphere.rules.sharding.tables.orders.table-strategy.standard.sharding-algorithm-name=time_range
复制代码


化疗方案

  • 时间维度分片(2020~2023 年度表)

  • 用户 ID 取模分库

  • 冷热分离(OSS 归档历史数据)


医嘱总结


优化三板斧

  1. 定位:慢查询日志+执行计划分析

  2. 切割:化繁为简拆分多步执行

  3. 重建:符合业务场景的数据结构


避坑口诀

  • 索引不是银弹,覆盖才是王道

  • Join 水深,能拆就拆

  • Order By+Limit≠分页优化


最后送上传秘方:当你优化 SQL 到怀疑人生时,不妨试试这三味药:

  1. 删业务逻辑

  2. 加缓存

  3. 换数据库

保证药到病除(老板打不打死你我就不管了,哈哈哈)!


文章转载自:苏三说技术

原文链接:https://www.cnblogs.com/12lisu/p/18873868

体验地址:http://www.jnpfsoft.com/?from=001YH

用户头像

还未添加个人签名 2025-04-01 加入

还未添加个人简介

评论

发布
暂无评论
如何医治一条慢SQL?_sql_量贩潮汐·WholesaleTide_InfoQ写作社区