2012-06-01 59 views
4

我有麻烦的MySQL查询性能。MySQL特定的查询性能调优

表(InnoDB的):

+--------------------+---------------------+------+-----+-------------------+-------+ 
| Field    | Type    | Null | Key | Default   | Extra | 
+--------------------+---------------------+------+-----+-------------------+-------+ 
| st_resource_id  | varchar(32)   | NO | MUL | NULL    |  | 
| st_sub_resource_id | varchar(32)   | YES |  | NULL    |  | 
| st_title   | varchar(500)  | YES |  | NULL    |  | 
| st_resource_type | varchar(100)  | NO | MUL | NULL    |  | 
| st_site_id   | tinyint(4)   | NO | MUL | NULL    |  | 
| st_time   | timestamp   | NO | MUL | CURRENT_TIMESTAMP |  | 
| st_user_id   | int(10) unsigned | YES |  | NULL    |  | 
| st_full_access  | tinyint(1) unsigned | YES |  | NULL    |  | 
+--------------------+---------------------+------+-----+-------------------+-------+ 

索引:

+---------------+------------+------------------+--------------+--------------------+-----------+-------------+----------+--------+------+------------+---------+ 
| 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 | 
+----+-------------+---------------+-------+-------------------------------------+-------------+---------+------+---------+----------------------------------------------+ 

表具有〜1,666,383行。查询运行速度非常慢。在MySQL进程列表中,我长时间(> 1分钟)在“复制到tmp表阶段”中看到此查询。查询会生成沉重的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; 

虽然问题得到解决(不漂亮,但有效的方式),我仍然有一些开放性的问题(见注释)。

回答

2

(st_site_id, st_resource_type, st_sub_resourse_id, st_time, st_resource_id)上创建一个复合索引。

但是,您仍然在计划中有temporaryfilesort,因为您在订购COUNT(*)时不能索引。

如果您需要快速和经常运行此查询,你就必须创造条件,存储计数为每个站点/资源/ subresourse /周组合,并在触发更新聚合表。

+0

,你告诉我,我已经创建复合索引。但MySQL仍然没有使用它。当我强制MySQL使用新的索引查询计划更改(请参阅我的问题更新)。但性能变得很棒!查询在0.0xxx秒内执行。对于这个建议很有帮助。 为什么MySQL不使用新索引automaticaly? 为什么4个独立索引不能被MySQL使用?它几乎与一个复合材料相同吗? –

+0

@ValeraLeontyev:一个复合索引与4个独立索引不完全相同。 4个索引不会形成您所有记录所在的单一范围。它们可以用来提高查询速度(使用'index_merge'),但只能在相等条件下使用,而且它似乎是对查询最有选择性的'st_time'谓词。 – Quassnoi

1

您是否尝试在st_resource_type, st_resource_id, st_time and st_site_id上创建复合索引?它看起来像你有几个索引,但大多数是在一个列或2列。通过使用更多列的组合索引可以提高性能。

0

在做多的where子句中编写他们应该与你写你的查询订单的订单查询。

在您的特定情况下,这将是:

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

这应该给你一个很好的速度提升。