特定于MySQL的查询性能调优

特定于MySQL的查询性能调优,mysql,performance,indexing,Mysql,Performance,Indexing,我在MySQL查询性能方面有问题 表(InnoDB): 索引: +---------------+------------+------------------+--------------+--------------------+-----------+-------------+----------+--------+------+------------+---------+ | Table | Non_unique | Key_name | Seq_in

我在MySQL查询性能方面有问题

表(InnoDB):

索引:

+---------------+------------+------------------+--------------+--------------------+-----------+-------------+----------+--------+------+------------+---------+
| Table         | Non_unique | Key_name         | Seq_in_index | Column_name        | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+---------------+------------+------------------+--------------+--------------------+-----------+-------------+----------+--------+------+------------+---------+
| nr_statistics |          1 | resource_id      |            1 | st_resource_id     | A         |     1546165 |     NULL | NULL   |      | BTREE      |         |
| nr_statistics |          1 | resource_id      |            2 | st_sub_resource_id | A         |     1546165 |     NULL | NULL   | YES  | BTREE      |         |
| nr_statistics |          1 | st_time          |            1 | st_time            | A         |     1546165 |     NULL | NULL   |      | BTREE      |         |
| nr_statistics |          1 | st_site_id       |            1 | st_site_id         | A         |          16 |     NULL | NULL   |      | BTREE      |         |
| nr_statistics |          1 | st_resource_type |            1 | st_resource_type   | A         |          16 |       10 | NULL   |      | BTREE      |         |
+---------------+------------+------------------+--------------+--------------------+-----------+-------------+----------+--------+------+------------+---------+
查询:

SELECT st_resource_id AS docId, count(*) AS cnt
FROM nr_statistics
WHERE
  st_resource_type = 'document'
  AND st_sub_resource_id = 'text'
  AND st_time > DATE_SUB(NOW(), INTERVAL 7 DAY)
  AND st_site_id = 1
GROUP BY st_resource_id
ORDER BY cnt DESC
LIMIT 0, 5;
查询计划:

+----+-------------+---------------+-------+-------------------------------------+-------------+---------+------+---------+----------------------------------------------+
| id | select_type | table         | type  | possible_keys                       | key         | key_len | ref  | rows    | Extra                                        |
+----+-------------+---------------+-------+-------------------------------------+-------------+---------+------+---------+----------------------------------------------+
|  1 | SIMPLE      | nr_statistics | index | st_time,st_site_id,st_resource_type | resource_id | 197     | NULL | 1581044 | Using where; Using temporary; Using filesort |
+----+-------------+---------------+-------+-------------------------------------+-------------+---------+------+---------+----------------------------------------------+
表有~1666383行。查询运行速度非常慢。在MySQL进程列表中,我在“复制到tmp表阶段”中看到这个查询很长一段时间(>1分钟)。查询生成沉重的I/O负载。我不明白如何解决问题并加快查询执行

如果问题是由错误的索引造成的,那么哪些索引是正确的

UPD.我创建了新的综合指数:

| nr_statistics |          1 | st_site_id_2     |            1 | st_site_id         | A         |          16 |     NULL | NULL   |      | BTREE      |         |
| nr_statistics |          1 | st_site_id_2     |            2 | st_resource_type   | A         |          16 |     NULL | NULL   |      | BTREE      |         |
| nr_statistics |          1 | st_site_id_2     |            3 | st_sub_resource_id | A         |      752018 |     NULL | NULL   | YES  | BTREE      |         |
| nr_statistics |          1 | st_site_id_2     |            4 | st_time            | A         |     1504037 |     NULL | NULL   |      | BTREE      |         |
| nr_statistics |          1 | st_site_id_2     |            5 | st_resource_id     | A         |     1504037 |     NULL | NULL   |      | BTREE      |         |
现在查询计划是:

+----+-------------+---------------+-------+---------------+--------------+---------+------+-------+-----------------------------------------------------------+
| id | select_type | table         | type  | possible_keys | key          | key_len | ref  | rows  | Extra                                                     |
+----+-------------+---------------+-------+---------------+--------------+---------+------+-------+-----------------------------------------------------------+
|  1 | SIMPLE      | nr_statistics | range | st_site_id_2  | st_site_id_2 | 406     | NULL | 21168 | Using where; Using index; Using temporary; Using filesort |
+----+-------------+---------------+-------+---------------+--------------+---------+------+-------+-----------------------------------------------------------+
查询现在运行得非常快(0.0x秒),但我必须强制使用新索引:

SELECT st_resource_id as docId, count( * ) AS Cnt
FROM nr_statistics
USE INDEX (st_site_id_2)
WHERE st_resource_type = 'document'
AND st_sub_resource_id = 'text'
AND st_time > DATE_SUB( NOW( ) , INTERVAL 7 DAY )
AND st_site_id = 1
GROUP BY st_resource_id
ORDER BY cnt DESC
LIMIT 0 , 5;

虽然问题得到了解决(虽然不是很漂亮,但很有效),但我仍然有一些悬而未决的问题(见评论)。

您是否尝试过在
st_资源类型、st_资源id、st_时间和st_站点id
上创建一个综合索引?在我看来,您有几个索引,但大多数索引都在一列上,或者可能在两列上。通过使用包含更多所需列的复合索引,可以提高性能。

上创建一个复合索引(st\u站点id、st\u资源类型、st\u子资源id、st\u时间、st\u资源id)

但是,您在计划中仍将有
临时
文件排序
,因为您是根据
计数(*)
进行排序的,这是不可索引的


如果需要快速且经常运行此查询,则必须创建一个聚合表,该表将存储每个站点/资源/子资源/周组合的计数,并在触发器中进行更新。

使用多个where子句进行查询时,写入它们的顺序应与写入查询的顺序相匹配

在您的特殊情况下,它将是:

CREATE INDEX stats_index ON nr_statistics (st_resource_type, st_sub_resource_id, st_time, st_site_id);

这会给你一个很好的提速。

我已经按照你说的创建了综合指数。但是MySQL仍然没有使用它。当我强制mysql使用新索引时,查询计划发生了变化(请参阅问题中的更新)。但是表演太棒了!查询在0.0xxx秒内执行。谢谢你的建议。为什么MySQL不自动使用新索引?为什么MySQL不能使用4个独立索引?它几乎和一个综合指数一样吗?@ValeraLeontyev:一个综合指数和四个独立指数不太一样。4索引并不是所有记录所在的单一范围。它们可以用来提高查询速度(使用
索引合并
),但只能在相等条件下使用,而且似乎是
st_time
谓词对您的查询最具选择性。
CREATE INDEX stats_index ON nr_statistics (st_resource_type, st_sub_resource_id, st_time, st_site_id);