聊聊我平时排查慢SQL的思路
经常有同行在群里问:「小马哥,慢SQL到底怎么查啊?」
说实话,这活儿我干了十年,闭着眼都能走完流程,但每次讲给新人听,他们还是一脸懵。后来我琢磨明白了——不是他们笨,是官方文档和那些教程,都把「排查思路」这层最关键的东西给省了,光甩命令,不给脑子。
今天不整虚的,我把平时那套方法完整捋一遍,配个前阵子的例子,你看完就能照葫芦画瓢。
先说个我一直坚持的小习惯
慢查询日志,我从入行第一天就开着。几个参数贴给你:
slow_query_log = ON
long_query_time = 1
log_queries_not_using_indexes = ON
long_query_time 我设 1 秒。别嫌这个数大,先跑起来,等你有感觉了再往下调。log_queries_not_using_indexes 我强烈建议开——有些SQL没超阈值,但压根没走索引,属于「迟早要出事」的那种,先记下来总没错。
别慌,有日志就能查。这句话我这些年说过八百遍。
当然我也见过不少环境,平时日志关着,出了事才想起来开——行吧,那你就只能去 SHOW PROCESSLIST 里碰运气了:
SHOW FULL PROCESSLIST;
看 Time 和 State 两列就行:Time 特别大、State 卡住不动的,八成就是它。把 Info 里的SQL抠出来。
前阵子的一个例子
有一次,业务方在群里@我,就一句话:「那个查订单的接口变慢了,你看下。」
你猜怎么着?没了,就这一句。SQL呢?不给你。环境呢?也不给你。就「你看下」三个字。
我一边翻白眼一边去捞日志,捞出来一条SQL,长这样:
SELECT * FROM order_info WHERE phone = 13800138000;
order_info 这张表几千万行,phone 字段上建了索引。看到这,你是不是已经觉得哪里不对劲了?
我当时第一反应是:索引都建了啊,怎么会慢?于是 EXPLAIN 一跑,关键几列长这样:
type: ALL
key: NULL
rows: 23641233
Extra: Using where
type 是 ALL,就是全表扫描,一行一行硬看;key 是 NULL,说明索引压根没被用上;rows 两千三百多万行。
好家伙,全表扫。我心里咯噔一下——不是没建索引,是索引根本没被用起来。
问题出在哪?用大白话讲
phone 这字段是 varchar,可SQL里写的是数字 13800138000。MySQL 一看,字符串列对上了数字值,怎么办?它就把每一行的字符串都转成数字再去比。
说白了就是:你以为它走索引,其实它在那吭哧吭哧把两千多万行挨个转类型。索引?废了。
这个坑我踩过不止一次,我敢说 80% 的「有索引但就是慢」的SQL,根子都在这类隐式类型转换上。而且它特别隐蔽——索引在、SQL看着也对,不跑个 EXPLAIN 你根本看不出来。
怎么修?
简单,把数字改成字符串,或者让开发传参数时按字符串传:
SELECT * FROM order_info WHERE phone = '13800138000';
再 EXPLAIN 一次,type 变成 ref(走索引了),key 用上了,rows 降到个位数,接口唰的一下就回来了。
除了这个,还有几个「有索引不走」的坑
- 索引列套函数:
WHERE DATE(create_time) = '2026-08-20',把函数摘了,改成范围查询。 - 前导模糊:
LIKE '%关键字',这种神仙来了也走不了索引。 - 联合索引没用最左前缀:你建了
(a,b,c),结果只用b去查,白搭。
复盘一下
这个例子本身不难,难在业务方就甩一句「慢了」。所以后来我给自己定了两条规矩:
- 报障必须带SQL,谁再只甩「慢了」俩字,我不理他(开玩笑,还是得理,但得补)。
- 慢查询日志常开、阈值设好。等出了事才想起来开日志,那才叫真正的「慢」。
排查慢SQL这件事,一半功夫在SQL本身,另一半在你平时有没有把日志和习惯养好。 这句话送给你,能省不少夜里的工夫。
最后多句嘴
慢SQL的原因,我归纳来归纳去就四类:没走索引、走了但扫描行数还是多、有排序临时表、还有一类最冤枉——它其实是在等锁,不是自己慢。所以看到 Time 大,别急着骂SQL写得烂,先看 State 是不是 Waiting for lock,骂错了人可就尴尬了。
总结一句话:找SQL、看计划、找原因、验证,这套流程你练熟,慢SQL这关就算过了。 希望你的生产环境,永远用不上我今天这套。
我是DBA小马哥,十年一线数据库运维。写的东西都是生产环境里趟出来的,关注我,少踩坑。
浙公网安备 33010602011771号