【问题标题】:Simple MySQL query with performance issues具有性能问题的简单 MySQL 查询
【发布时间】:2012-10-16 10:41:06
【问题描述】:

我有以下简单的 MySQL 查询:

SELECT SQL_NO_CACHE mainID
FROM tableName 
WHERE otherID3=19
AND dateStartCol >= '2012-08-01' 
AND dateStartCol <= '2012-08-31';

当我运行它时,需要 0.29 秒才能带回 36074 个结果。当我增加我的日期周期以带回更多结果(65703)时,它运行在 0.56 中。当我在同一台服务器上但在不同的表(有些表更大)上运行其他类似的 SQL 查询时,结果会在大约 0.01 秒内返回。

虽然 0.29 并不慢 - 这是复杂查询的基本部分,而且这个时间意味着它不可扩展。

请参阅下面的表定义和索引。

我知道这不是服务器负载,因为我在使用很少的开发服务器上遇到了同样的问题。

+---------------------------+--------------+------+-----+---------+----------------+
| Field                     | Type         | Null | Key | Default | Extra          |
+---------------------------+--------------+------+-----+---------+----------------+
| mainID                    | int(11)      | NO   | PRI | NULL    | auto_increment |
| otherID1                  | int(11)      | NO   | MUL | NULL    |                |
| otherID2                  | int(11)      | NO   | MUL | NULL    |                |
| otherID3                  | int(11)      | NO   | MUL | NULL    |                |
| keyword                   | varchar(200) | NO   | MUL | NULL    |                |
| dateStartCol              | date         | NO   | MUL | NULL    |                |
| timeStartCol              | time         | NO   | MUL | NULL    |                |
| dateEndCol                | date         | NO   | MUL | NULL    |                |
| timeEndCol                | time         | NO   | MUL | NULL    |                |
| statusCode                | int(1)       | NO   | MUL | NULL    |                |
| uRL                       | text         | NO   |     | NULL    |                |
| hostname                  | varchar(200) | YES  | MUL | NULL    |                |
| IPAddress                 | varchar(25)  | YES  |     | NULL    |                |
| cookieVal                 | varchar(100) | NO   |     | NULL    |                |
| keywordVal                | varchar(60)  | NO   |     | NULL    |                |
| dateTimeCol               | datetime     | NO   | MUL | NULL    |                |
+---------------------------+--------------+------+-----+---------+----------------+


+--------------------+------------+-------------------------------+--------------+---------------------------+-----------+-------------+----------+--------+------+------------+---------+
| Table              | Non_unique | Key_name                      | Seq_in_index | Column_name               | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+--------------------+------------+-------------------------------+--------------+---------------------------+-----------+-------------+----------+--------+------+------------+---------+
| tableName          |          0 | PRIMARY                       |            1 | mainID                    | A         |      661990 |     NULL | NULL   |      | BTREE      |         |
| tableName          |          1 | idx_otherID1                  |            1 | otherID1                   | A         |      330995 |     NULL | NULL   |      | BTREE      |         |
| tableName          |          1 | idx_otherID2                  |            1 | otherID2                   | A         |          25 |     NULL | NULL   |      | BTREE      |         |
| tableName          |          1 | idx_otherID3                  |            1 | otherID3                   | A         |          48 |     NULL | NULL   |      | BTREE      |         |
| tableName          |          1 | idx_dateStartCol              |            1 | dateStartCol               | A         |         187 |     NULL | NULL   |      | BTREE      |         |
| tableName          |          1 | idx_timeStartCol              |            1 | timeStartCol               | A         |       73554 |     NULL | NULL   |      | BTREE      |         |
|tableName          |          1 | idx_dateEndCol                 |            1 | dateEndCol                 | A         |         188 |     NULL | NULL   |      | BTREE      |         |
|tableName          |          1 | idx_timeEndCol                 |            1 | timeEndCol                 | A         |       73554 |     NULL | NULL   |      | BTREE      |         |
| tableName          |          1 | idx_keyword                   |            1 | keyword                    | A         |       82748 |     NULL | NULL   |      | BTREE      |         |
| tableName           |          1 | idx_hostname                 |            1 | hostname                   | A         |        2955 |     NULL | NULL   | YES  | BTREE      |         |
| tableName           |          1 | idx_dateTimeCol              |            1 | dateTimeCol                | A         |      220663 |     NULL | NULL   |      | BTREE      |         |
| tableName           |          1 | idx_statusCode               |            1 | statusCode                 | A         |           2 |     NULL | NULL   |      | BTREE      |         |
+--------------------+------------+-------------------------------+--------------+---------------------------+-----------+-------------+----------+--------+------+------------+---------+

解释输出:

+----+-------------+-----------+-------+----------------------------------+-------------------+---------+------+-------+----------+-------------+
| id | select_type | table     | type  | possible_keys                    | key               | key_len | ref  | rows  | filtered | Extra       |
+----+-------------+-----------+-------+----------------------------------+-------------------+---------+------+-------+----------+-------------+
|  1 | SIMPLE      | tableName | range | idx_otherID3,idx_dateStartCol | idx_dateStartCol | 3       | NULL | 66875 |    75.00 | Using where |
+----+-------------+-----------+-------+----------------------------------+-------------------+---------+------+-------+----------+-------------+

【问题讨论】:

  • 你试过AND dateStartCol BETWEEN '2012-08-01' AND '2012-08-31'。不确定它是否会有所帮助,但值得一试。
  • 我已经尝试了 BETWEEN 并且时间没有显着差异(从 0.29 到 0.28)。 - 谢谢你的建议。

标签: mysql performance


【解决方案1】:

如果这确实是您的查询(而不是相同的简化版本),那么这应该可以达到最佳结果:

 CREATE INDEX table_ndx on tableName( otherID3, dateStartCol, mainID);

第一个索引条目表示WHERE中的第一个匹配非常快;这同样适用于dateStartCol。第三个字段非常小,不会显着降低索引速度,但允许在索引中立即找到您需要的数据,而根本不需要表访问。

键在相同索引中很重要。在您发布的EXPLAIN 中,每个键都在自己的索引中,因此即使 MySQL 选择最佳索引,性能也不会最佳。我会尝试使用更少的索引,因为它们也有成本(无耻的插头:Can Indices actually decrease SELECT performance?)。

【讨论】:

    【解决方案2】:

    首先尝试添加正确的键。似乎 dateStartCol 比 otherID3 更具选择性

    ALTER TABLE tableName ADD KEY idx_dates(dateStartCol, dateStartCol)
    

    第二 - 请确保通过将 LIMIT 子句添加到 SELECT 来仅选择所需的行。这将提高查询。试试这样:

    SELECT SQL_NO_CACHE mainID FROM 表名 其他ID3=19 AND dateStartCol >= '2012-08-01' AND dateStartCol

    还请确保您的 MySQL 已正确调整。您可能需要检查 key_buffer_size 和 innodb_buffer_pool_size,如http://astellar.com/2011/12/why-is-stock-mysql-slow/中所述

    【讨论】:

    • LIMIT 不会有太大区别,因为它会先执行相同的查询,然后只返回部分结果。由于这只是对几个 ID 的查询,因此数据传输不会是昂贵的部分。
    • 您对数据传输的看法是正确的,但在某些情况下,当达到限制时 MySQL 会停止读取数据。似乎 IO 在这种情况下涉及很多,它可能会有所帮助。但无论如何 300 毫秒对于这种类型的查询来说太长了,不管 LIMIT 是多少
    【解决方案3】:

    如果这是一个经常性或重要的查询,则创建一个多列索引:

    CREATE INDEX index_name ON tableName (otherID3, dateStartCol)
    

    删除未使用的索引,因为它们会使表更改成本更高。

    顺便说一句,您不需要两个单独的日期和时间列。然后,您可以组合成 datetimetimestamp 类型。少一列,少一索引。

    explain 输出显示它选择了dateStartCol 索引,因此您可以尝试我上面建议的相反方法:

    CREATE INDEX index_name ON tableName (dateStartCol, otherID3)
    

    请注意,查询的 dateStartCol 条件仍将获得 75% 的行,因此在使用该单一索引时没有太大改进(如果有的话)。

    otherID3 有多独特?如果没有太多重复的otherID3你可以hint引擎来使用它。

    【讨论】:

    • 我试过这样做,但是查询时间从 0.29 到 0.25。还使用了所有索引 - 不一定在此 SQL 中,但在其他报告中,因此它们不能被删除。
    • @mh1 编辑基于explain
    • 确实,应该这样做。但是,我很确定该索引中字段的顺序无关紧要,因为在 AND 查询中 MySQL 可以决定重新排序它们。如果这有所作为,它甚至可能是一个错误。
    • @DanielSchneller 我很确定订单很重要。编辑hint
    • 再想想,你可能是对的。如果其中一个字段比另一个字段具有更好的基数(即更多不同的值,因此更好地消除候选者),如果更具体的列不是索引中的第一个列,MySQL 可能会决定忽略组合索引,并且完全选择不同的索引。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-06
    • 2015-07-12
    • 1970-01-01
    • 2021-07-10
    • 1970-01-01
    • 2014-02-18
    相关资源
    最近更新 更多