订购空集时,SQL Query变得异常缓慢

时间:2016-10-26 14:00:12

标签: mysql sql

我有一个SQL查询需要花费大量时间来评估,因为它在一个非常大的数据集上运行。在尝试改善执行时间时,我发现了以下内容:

执行以下查询时,MySQL服务器需要花费大量时间(最多100秒)

SELECT some_data 
FROM   table 
       INNER JOIN anothertable 
               ON ( table.value = 
                               anothertable.value ) 
WHERE  ( table.parent = 56521 
         AND table.date >= 
             '2016-10-19 08:37:45.606947' ) 
ORDER  BY table.date DESC 
LIMIT  1

所以我猜测它是查询的排序部分需要花费如此多的执行时间,我手动删除以排序以查看执行中的差异:

SELECT some_data 
FROM   table 
       INNER JOIN anothertable 
               ON ( table.value = 
                               anothertable.value ) 
WHERE  ( table.parent = 56521 
         AND table.date >= 
             '2016-10-19 08:37:45.606947' ) 
LIMIT  1

上面的查询需要0.45秒,并导致一个空的查询集。

我得出结论,我的查询在评估WHERE-Clause之前命令WHOLE数据集。我应该如何形成查询以防止这种行为?为什么会出现这种行为?

这些是用于慢速和快速查询的EXPLAIN表:

Slow
+----+-------------+-------+------------+--------+------------------------------------------+------------------+---------+------------------------------+------+----------+-------------+
    | id | select_type | table | partitions | type   | possible_keys                            | key              | key_len | ref                          | rows | filtered | Extra       |
    +----+-------------+-------+------------+--------+------------------------------------------+------------------+---------+------------------------------+------+----------+-------------+
    |  1 | SIMPLE      | A     | NULL       | index  | PRIMARY,D4b797d14e515242e7251754c57b7701 | date             | 5       | NULL                         | 1325 |     0.08 | Using where |
    |  1 | SIMPLE      | B     | NULL       | eq_ref | PRIMARY                                  | PRIMARY          | 4       | value                        |    1 |   100.00 | NULL        |
    +----+-------------+-------+------------+--------+------------------------------------------+------------------+---------+------------------------------+------+----------+-------------+

Fast:
     +----+-------------+-------+------------+--------+------------------------------------------+----------------------------------+---------+------------------------------+------+----------+-------+
    | id | select_type | table | partitions | type   | possible_keys                            | key                              | key_len | ref                          | rows | filtered | Extra |
    +----+-------------+-------+------------+--------+------------------------------------------+----------------------------------+---------+------------------------------+------+----------+-------+
    |  1 | SIMPLE      | A     | NULL       | ref    | PRIMARY,D4b797d14e515242e7251754c57b7701 | D4b797d14e515242e7251754c57b7701 | 4       | const                        | 5175 |   100.00 | NULL  |
    |  1 | SIMPLE      | B     | NULL       | eq_ref | PRIMARY                                  | PRIMARY                          | 4       | value                        |    1 |   100.00 | NULL  |
    +----+-------------+-------+------------+--------+------------------------------------------+----------------------------------+---------+------------------------------+------+----------+-------+

1 个答案:

答案 0 :(得分:1)

MySQL使用date上的索引进行第一次查询。它可以部分评估where - 条件(table.date >= '2016-10-19 08:37:45.606947'),如果合适,它将从您的表中读取parent(这相对较慢),看它是否也适合。它可以在找到结果后立即停止(因为order bylimit 1)。

您的第二个查询使用parent上的索引(即具有长名称的索引),查找适合的行,然后从表中读取date - 部分并检查它是否适合太。它必须继续,直到它使用正确的parent - 值(它使用索引找到)检查所有行,并且它找到的所有行都必须经历一个filesort,并且将返回最新的行。

(我忽略了MySQL也必须检查/执行join,但在两个查询中都是一样的。)

你显然有更多符合date条件的行,而不是parent - 条件,所以它必须做更多相对较慢的表查找,这需要更长的时间。

在这种情况下。根据您的数据,实际上可能会发生通过date上的索引检查的第一行已经满足parent条件,并且可能会在那里停止。如果它将使用parent上的索引,MySQL将被强制检查具有parent - 值的所有行,然后执行文件排序。 MySQL在一些统计数据的基础上决定,值得冒这个风险。好吧,它选错了。

您可以执行以下操作:

  • optimize table `table`(第二个table是您的表名)来更新您的统计信息。这有时会有所帮助,但通常不会(因为统计数据非常有限)。
  • 强制MySQL使用您认为更好的索引(... FROM table force index (D4b797d14e515242e7251754c57b7701) inner join ...
  • 为您的查询添加完美索引:复合索引table(parent, date)应该(不计算join的潜在影响)给出比无序查询更快的结果,MySQL将使用它它自己的。