Mysql慢查询

在一次线上故障调查时,偶然发现Mysql的慢查询,仔细研究发现只要是返回了某个特定的字段,查询就会变成很慢。

mysql> SELECT id  FROM `task_job` WHERE (`task_job`.`is_deleted` = 0 AND `task_job`.`is_hidden` = 0 AND `task_job`.`status` IN (30, 32, 31) AND `task_job`.`valid_status` = 10);
Empty set (0.22 sec)

mysql> SELECT id, waf_config  FROM `task_job` WHERE (`task_job`.`is_deleted` = 0 AND `task_job`.`is_hidden` = 0 AND `task_job`.`status` IN (30, 32, 31) AND `task_job`.`valid_status` = 10);
Empty set (10.03 sec)

可以看到,仅仅是在返回的字段中增加了一个 waf_config 字段,查询时间就从 0.22秒 变成了 10.03秒,而查询出来的都是没有满足条件的数据。

表中的 waf_config 字段为一个 json 字段,它的平均大小为29K。

mysql> select json_storage_size(waf_config) from task_job where waf_config is not null limit 5;
+-------------------------------+
| json_storage_size(waf_config) |
+-------------------------------+
|                         29886 |
|                         29886 |
|                         29886 |
|                         29886 |
|                         29886 |
+-------------------------------+
5 rows in set (0.02 sec)

通过EXPLAIN可以得知,Mysql在查询时会使用 status 字段的索引,而不是全表扫描。

mysql> EXPLAIN SELECT id, waf_config  FROM `task_job` WHERE (`task_job`.`is_deleted` = 0 AND `task_job`.`is_hidden` = 0 AND `task_job`.`status` IN (30, 32, 31) AND `task_job`.`valid_status` = 10);
+----+-------------+----------+------------+-------+---------------------+---------------------+---------+------+--------+----------+------------------------------------+
| id | select_type | table    | partitions | type  | possible_keys       | key                 | key_len | ref  | rows   | filtered | Extra                              |
+----+-------------+----------+------------+-------+---------------------+---------------------+---------+------+--------+----------+------------------------------------+
|  1 | SIMPLE      | task_job | NULL       | range | task_job_idx_status | task_job_idx_status | 3       | NULL | 157831 |     0.10 | Using index condition; Using where |
+----+-------------+----------+------------+-------+---------------------+---------------------+---------+------+--------+----------+------------------------------------+
1 row in set, 1 warning (0.00 sec)

但由于查询条件中涉及到了其它字段,Mysql不能直接从索引得到判断,所以只能先查询索引,然后将满足条件的记录加载,再对其它字段进行条件判断。

而由于waf_config字段特别大,导致加载数据消耗大量时间。

作为验证,在测试通过索引可以直接判断出结果的查询中,即便返回字段包括waf_config,查询依然很快。

mysql> select id, waf_config from task_job where id = -1;
Empty set (0.00 sec)
comments powered by Disqus