跳到主要内容
弗美科技
技术分享

2000 行数据也卡,第一次认真看 MySQL 慢查询

表里才 2000 多行,点一次等三四秒。翻慢查询日志才查出五处:long_query_time 默认 10 秒根本不记、OFFSET 1900 白扫前 1900 行、时间字段套 DATE() 让索引失效、JOIN 两边 INT 与 VARCHAR 不一致、SELECT * 捎回两个 TEXT。没加缓存,看完 EXPLAIN 就好了。

2016年12月19日数据库索引优化踩坑记录

一个内部用的查询页,表里才 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。数据量小不代表不会慢,这句话是那次记住的。

商务咨询 · 免费初步沟通

需要针对你的场景给个建议?

把业务背景和当前系统情况告诉我们,我们会给出可执行的思路。

在线留言

周一至周五 9:00–18:00 · 工作日 9:00–18:00 响应,留言会在一个工作日内回复。

商务微信
商务微信二维码

扫一扫,加商务微信咨询服务与方案报价

或填写需求表单