【问题标题】:MySQL specific query performance tuningMySQL 特定查询性能调优
【发布时间】:2012-06-01 12:20:39
【问题描述】:

我在 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 进程列表中,我在“复制到 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;

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

【问题讨论】:

    标签: mysql performance indexing


    【解决方案1】:

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

    但是,您仍然会在计划中包含 temporaryfilesort,因为您是在不可索引的 COUNT(*) 上订购的。

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

    【讨论】:

    • 我已经按照你说的创建了复合索引。但是 MySQL 仍然没有使用它。当我强制 mysql 使用新的索引时,查询计划发生了变化(请参阅我的问题中的更新)。但是演技太棒了!查询在 0.0xxx 秒内执行。非常感谢您的建议。为什么 MySQL 不自动使用新索引? MySQL为什么不能使用4个独立索引?和一个复合材料几乎一样吗?
    • @ValeraLeontyev:一个复合索引与 4 个独立索引不太一样。 4 个索引不会形成所有记录所在的单个范围。它们可用于提高查询速度(使用index_merge),但仅限于相等条件,而且似乎st_time 谓词对您的查询最具选择性。
    【解决方案2】:

    您是否尝试在 st_resource_type, st_resource_id, st_time and st_site_id 上创建复合索引?在我看来,您有多个索引,但大多数索引都在单个列上,或者可能是 2 列。通过使用包含更多您需要的列的复合索引,它可能会提高性能。

    【讨论】:

      【解决方案3】:

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

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

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2022-07-26
        • 1970-01-01
        • 1970-01-01
        • 2016-06-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多