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)