如何医治一条慢SQL?
前言
"苏工,订单列表又崩了!"
接到电话时,我对着监控大屏上999ms的SQL响应时间哭笑不得。
几年来,我发现一个定律:所有SQL问题都是在凌晨三点爆发!
今天抽丝剥茧,教你用架构师的思维给慢SQL开刀手术。
希望对你会有所帮助。
1 术前检查:找准病灶
1.1 EXPLAIN 查看执行计划
使用EXPLAIN查看SQL语句的执行计划,相当于给SQL拍了张X光。
下面是一个典型的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关键字查看执行计划的结果如下:
+----+-------------+-------+------+---------------+------+---------+------+---------+---------------------------------+
| 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_usage
WHERE 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);
消毒方案:
- SQL审核平台接入(如Yearning)
- MyBatis拦截器拦截全表更新
- 自动化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_time
spring.shardingsphere.rules.sharding.tables.orders.table-strategy.standard.sharding-algorithm-name=time_range
化疗方案:
- 时间维度分片(2020~2023年度表)
- 用户ID取模分库
- 冷热分离(OSS归档历史数据)
医嘱总结
优化三板斧:
- 定位:慢查询日志+执行计划分析
- 切割:化繁为简拆分多步执行
- 重建:符合业务场景的数据结构
避坑口诀:
- 索引不是银弹,覆盖才是王道
- Join水深,能拆就拆
- Order By+Limit≠分页优化
最后送上苏三的传秘方:当你优化SQL到怀疑人生时,不妨试试这三味药:
- 删业务逻辑
- 加缓存
- 换数据库
保证药到病除(老板打不打死你我就不管了,哈哈哈)!
最后说一句(求关注,别白嫖我)
如果这篇文章对您有所帮助,或者有所启发的话,帮忙关注一下我的同名公众号:苏三说技术,您的支持是我坚持写作最大的动力。
求一键三连:点赞、转发、在看。
关注公众号:【苏三说技术】,在公众号中回复:进大厂,可以免费获取我最近整理的10万字的面试宝典,好多小伙伴靠这个宝典拿到了多家大厂的offer。
如何医治一条慢SQL?的更多相关文章
- php向队列服务里插入一条insert sql例如
		Iserver简介 Iserver是一个用python编写的网络服务框架(编译版本3.4.1),使用的是epool网络模型 测试机配置 处理器 2x Genuine Intel(R) CPU T205 ... 
- 一条查询sql的执行流程和底层原理
		1.一条查询SQL执行流程图 2.查询SQL执行流程之发送SQL请求 (1)客户端按照Mysql通信协议将SQL发送到服务端,SQL到达服务端后,服务端会单起一个线程执行SQL. (2)执行时Mysq ... 
- 面试官:说说一条查询sql的执行流程和底层原理?
		一条查询SQL执行流程图如下 序章 自我介绍 我是一条sql,就是一条长长的字符串,不要问我长什么样,因为我比较傲娇. 额~~不是我不说啊,因为细说起来,我可以细分为DML(Update.Insert ... 
- mssql sqlserver两条求和sql脚本相加的方法分享
		转自:http://www.maomao365.com/?p=7205 摘要: 下文分享两条sql求和脚本,再次求和的方法分享 /* 例: 下文已知两条sql求和脚本,现需对两张不同表的求和记录再次求 ... 
- sql去重;同一条数据出现多条取一条的sql语句
		理论上相同数据个别字段值不同重复问题: 1.某字段重复,其他字段值不同时,按重复字段分组只取一条的sql语句(eg:相同的数据某个字段值有差别导致存储两条或多条无意义重复数据的情况)select s. ... 
- Oracle随机选择一条记录SQL
		Oracle随机选择一条记录SQL: 
- Oracle取查询结果数据的第一条记录SQL
		Oracle取查询结果数据的第一条记录SQL: ; ; 
- MySql 学习之 一条更新sql的执行过程
		上一篇文章咱们说了一条查询sql的执行过程.如果没有看过上一篇文章的可以去看下上一篇文章,今天咱们说说一条更新sql的执行过程. 上面一条sql是将id为1的分数加上10. 那么它的执行流程是怎样的呢 ... 
- MySql 学习之 一条查询sql的执行过程
		相信大家都接触过Mysql数据库,而且也肯定都会写sql.我不知道大家有没有这样的感受,反正我是有过这样的想法.就是当我把一条sql语句写完了,并且执行完得到想要的结果.这时我就在想为什么我写这样的一 ... 
- 一条查询SQl是怎样执行的
		MySQL的逻辑架构图 大体来说,MySQL可以分为Server层和存储引擎层两部分. Server层包括连接器.查询缓存.分析器,优化器等,涵盖MySQL的大多核心服务功能,以及所有的内置函数,存储 ... 
随机推荐
- 用 Emacs 写代码有哪些值得推荐的插件
			以下是一些用于 Emacs 写代码的值得推荐的插件: Ido-mode:交互式操作模式,它用列出当前目录所有文件的列表来取代常规的打开文件提示符,能让操作更可视化,快速遍历文件. Smex:可替代普通 ... 
- DW002 - 数据仓库模型设计
			数据模型 关系模型与维度模型 常见数据模型设计方法 数据模型 1. 什么是数据模型 模型 - Model 模型是指对于某个实际问题或者客观事物.规律进行抽象后的一种形式化表达方式 比如地图.建筑设计沙 ... 
- DW001 - 数据仓库理论知识
			数据仓库概念 数据仓库基本架构 数据集市概念 数据湖概念 数据仓库概念 数据仓库(Data Warehouse,DW)是一个面向主题的.集成的.非易失的.反映历史变化的.用来支持企业管理决策的数据集合 ... 
- Linux - 基础环境检查
			检查操作系统:建议根据实际产品需要进行安装 检查主机名:集群中统一前缀并区分服务器功能,小写命名 检查内存:建议至少128G 检查CPU:建议至少2个支持超线程技术的10核芯片 检查磁盘:同一功能的服 ... 
- 启动本地node服务器报错: Access denied for user ‘root‘@‘localhost‘ (using password: YES)
			背景:今天启动node服务时直接报错,顿时一激灵,之前(几个月前哈哈)明明好好的.主要问题就是在连接数据库上,我登上mysql瞅瞅有没有问题,当要输入密码时,emmm, 很好, 忘记root密码了,于 ... 
- 国内四大骨干网与十大ISP服务商
			1.骨干网 几台计算机连接起来,互相可以看到其他人的文件,这叫局域网,整个城市的计算机都连接起来,就是城域网,把城市之间连接起来的网就叫骨干网.这些骨干网是国家批准的可以直接和国外连接的互联网.其他有 ... 
- 【Azure Developer】分享两段Python代码处理表格(CSV格式)数据 : 根据每列的内容生成SQL语句
			问题描述 在处理一个数据收集工作任务上,收集到的数据内容格式都不能直接对应到数据库中的表格内容. 比如: 第一种情况:服务名作为第一列内容,然后之后每一列为一个人名,1:代表此人拥有这个服务,0:代表 ... 
- 《视觉SLAM十四讲》第13讲 设计SLAM系统 回环检测线程的实现
			<视觉SLAM十四讲>第13讲 设计SLAM系统 回环检测线程的实现 这个学期看完了高翔老师的<视觉SLAM十四讲>,学到了很多,首先是对计算机视觉的基本知识有了一个更加全面系 ... 
- HashMap 在高并发场景下可能出现的性能问题以及如何规避这些问题
			JDK1.8 之前 HashMap 底层是 数组和链表, 之后在之前基础上加上红黑树. 相比于之前的版本, JDK1.8 之后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为 8)(将链表转 ... 
- 基于.NetCore开发 StarBlog 番外篇 (1) StarBlog Publisher,跨平台一键发布,DeepSeek加持的文章创作神器
			前言 我一直在优化发布文章的工作流 之前的 StarBlog 已经支持文章打包上传(将 Markdown 和图片文件一并打包为 ZIP 格式上传),但还是有不少步骤,重复的次数多了,还是感觉麻烦. 为 ... 
