一个内部用的查询页,表里才 2000 多行,点一次要等三四秒。2016 年 12 月,我们第一次为这种「数据量根本不大」的慢去翻慢查询日志。记录一下当时查出来的几条,都是很初级的坑。
1. 开了慢查询日志,但阈值是默认的 10 秒
long_query_time 默认 10,三四秒的根本不记。先 SET GLOBAL long_query_time = 1,再把 slow_query_log 打开,复现一次,才看到语句。这一步耗了小半天,因为一开始以为没开成功,反复重启了两次 mysqld。
2. 分页用了 OFFSET 1900
列表页直接 LIMIT 20 OFFSET 1900,MySQL 得先扫过前 1900 行。2000 行数据也要扫 1900 行再丢掉,翻到最后一页最慢。临时改成按 id 范围取,配合上一页最大 id,后面再说。
3. 时间字段上套了函数
条件是 WHERE DATE(create_time) = '2016-12-01'。create_time 建了索引,但套上 DATE() 之后索引就用不上了,全表扫。改成 create_time >= '2016-12-01 00:00:00' AND create_time < '2016-12-02 00:00:00',同样的结果,走索引。这条是拿 EXPLAIN 看出来的,type 从 ALL 变成 range,rows 从 2000 多掉到 30 几。
4. 关联字段类型不一致
主表 user_id 是 INT,另一张表的 user_id 是 VARCHAR(20)。JOIN 的时候 MySQL 要隐式转换,索引又废一次。把字段类型统一成 INT,改了 3 张表,顺便补了两个联合索引。
5. SELECT * 带了两个 TEXT 字段
列表页其实只显示 5 列,但查询写的是 SELECT *,把备注和附件路径两个 TEXT 一起捞回来,网络传输和临时表都变重。改成明确列出那 5 个字段名。
结果
五处改完,那个页面从三四秒到基本点开就有。没加缓存,没换硬件,就是老老实实看了一遍 EXPLAIN。
后来我们把 EXPLAIN 贴进了代码评审的 checklist。数据量小不代表不会慢,这句话是那次记住的。

