如何医治一条慢SQL?
笔记哥 /
05-13 /
36点赞 /
0评论 /
998阅读
## 1 术前检查:找准病灶
### 1.1 EXPLAIN 查看执行计划
使用EXPLAIN查看SQL语句的执行计划,相当于给SQL拍了张X光。
下面是一个典型的SQL问题,它是某电商平台历史订单查询的SQL语句:
```sql
SELECT *
FROM orders o
LEFT JOIN users u ON o.user_id = u.id
LEFT JOIN products p ON o.product_id = p.id
WHERE 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关键字查看执行计划的结果如下:
```sql
+----+-------------+-------+------+---------------+------+---------+------+---------+---------------------------------+
| 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字段索引失效
错误用法:
```sql
ALTER TABLE users ADD INDEX idx_extend ((extend_info->'$.is_vip'));
```
extend\_info字段是JSON类型的字段,即使创建了索引,索引也会丢失。
正解姿势(MySQL 8.0+):
```sql
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顺序是:
```sql
orders → users → products
```
优化后的方案:
```sql
(子查询过滤users) → products → orders
```
调整执行顺序,用小表驱动大表。
重写后的SQL:
```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强制指定索引:
```sql
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';
```
使用衍生表加速:
```sql
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 生命体征监测
查看索引使用:
```sql
SHOW INDEX FROM orders;
```
监控索引使用率:
```csharp
SELECT object_schema, object_name, index_name,
count_read, count_fetch
FROM performance_schema.table_io_waits_summary_by_index_usage
WHERE index_name IS NOT NULL;
```
## 3 术后护理:体系化治理
### 3.1 SQL消毒中心
需要制定优秀的代码规范,否则可能会出现全表扫描的问题。
在日常工作中,我们要尽可能减少**Java代码感染源**。
MyBatis危险写法:
```java
@Select("SELECT * FROM orders WHERE #{condition}")
List findByCondition(@Param("condition") String condition);
```
condition参数可以传入任何内容,如何传入了1=1,可能会导致查询所有的数据,走全表扫描,让查询效率变得非常低。
正确做法(参数化查询):
```csharp
@Select("SELECT * FROM orders WHERE create_time > #{time}")
List findByTime(@Param("time") Date time);
```
**消毒方案**:
1. SQL审核平台接入(如Yearning)
2. MyBatis拦截器拦截全表更新
3. 自动化EXPLAIN分析流水线
### 3.2 查杀大表癌症
如果遇到大表的癌症病例,可以用分库分表的方案解决。
**病历案例**:3亿订单表终极解决方案
```yml
// 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_time
spring.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. 换数据库
本文来自投稿,不代表本站立场,如若转载,请注明出处:http//www.knowhub.vip/share/2/3326
- 热门的技术博文分享
- 1 . ESP实现Web服务器
- 2 . 从零到一:打造高效的金仓社区 API 集成到 MCP 服务方案
- 3 . 使用C#构建一个同时问多个LLM并总结的小工具
- 4 . .NET 原生驾驭 AI 新基建实战系列Milvus ── 大规模 AI 应用的向量数据库首选
- 5 . 在Avalonia/C#中使用依赖注入过程记录
- 6 . [设计模式/Java] 设计模式之工厂方法模式
- 7 . 5. RabbitMQ 消息队列中 Exchanges(交换机) 的详细说明
- 8 . SQL 中的各种连接 JOIN 的区别总结!
- 9 . JavaScript 中防抖和节流的多种实现方式及应用场景
- 10 . SaltStack 远程命令执行中文乱码问题
- 11 . 推荐10个 DeepSeek 神级提示词,建议搜藏起来使用
- 12 . C#基础:枚举、数组、类型、函数等解析
- 13 . VMware平台的Ubuntu部署完全分布式Hadoop环境
- 14 . C# 多项目打包时如何将项目引用转为包依赖
- 15 . Chrome 135 版本开发者工具(DevTools)更新内容
- 16 . 从零创建npm依赖,只需执行一条命令
- 17 . 关于 Newtonsoft.Json 和 System.Text.Json 混用导致的的序列化不识别的问题
- 18 . 大模型微调实战之训练数据集准备的艺术与科学
- 19 . Windows快速安装MongoDB之Mongo实战
- 20 . 探索 C# 14 新功能:实用特性为编程带来便利
- 相关联分享
- 如何医治一条慢SQL?