mysql扫描行数的差异

时间:2017-03-28 07:35:58

标签: mysql query-optimization sql-execution-plan explain

我有一个问题:

mysql> explain SELECT  *
    FROM  OTHERS_TINY_URL_TBL
    WHERE  LINK_TYPE = 'BITLY'
      AND  URL_SHORTNER_ID = '5434e3b9e4b03aa06f25da11'
      AND  MODIFIED_TM >= '2016-03-13 21:09:43'
      AND  MODIFIED_TM <= '2017-03-13 21:09:43'
      AND  POST_ID < 0
    ORDER BY  MODIFIED_TM DESC
    LIMIT  1000\G;
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: OTHERS_TINY_URL_TBL
         type: range
possible_keys: XIE1_OTHERS_TINY_URL_TBL,XIE2_OTHERS_TINY_URL_TBL,XIE5_OTHERS_TINY_URL_TBL
          key: XIE5_OTHERS_TINY_URL_TBL
      key_len: 4
          ref: NULL
         rows: 47168
        Extra: Using index condition; Using where
1 row in set (0.00 sec)

ERROR: 
No query specified

我不知道为什么扫描这么多行。似乎ORDER BY和LIMIT使其效率低下

没有ORDER BY和LIMIT:

mysql> explain SELECT  *
    FROM  OTHERS_TINY_URL_TBL
    WHERE  LINK_TYPE = 'BITLY'
      AND  URL_SHORTNER_ID = '5434e3b9e4b03aa06f25da11'
      AND  MODIFIED_TM >= '2016-03-13 21:09:43'
      AND  MODIFIED_TM <= '2017-03-13 21:09:43'
      AND  POST_ID < 0\G;
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: OTHERS_TINY_URL_TBL
         type: range
possible_keys: XIE1_OTHERS_TINY_URL_TBL,XIE2_OTHERS_TINY_URL_TBL,XIE5_OTHERS_TINY_URL_TBL
          key: XIE2_OTHERS_TINY_URL_TBL
      key_len: 9
          ref: NULL
         rows: 4950
        Extra: Using index condition; Using where
1 row in set (0.00 sec)

ERROR: 
No query specified

过滤集中只有85行:

mysql>  SELECT  count(*)
    FROM  OTHERS_TINY_URL_TBL
    WHERE  LINK_TYPE = 'BITLY'
      AND  URL_SHORTNER_ID = '5434e3b9e4b03aa06f25da11'
      AND  MODIFIED_TM >= '2016-03-13 21:09:43'
      AND  MODIFIED_TM <= '2017-03-13 21:09:43'
      AND  POST_ID < 0\G;
*************************** 1. row ***************************
count(*): 85
1 row in set (0.02 sec)

ERROR: 
No query specified

创建在较少行数上扫描的嵌套查询:

mysql> explain SELECT  *
    FROM  
    (
        SELECT  *
            from  OTHERS_TINY_URL_TBL
            where  URL_SHORTNER_ID = '5434e3b9e4b03aa06f25da11'
              AND  MODIFIED_TM >= '2016-03-13 21:09:43'
              AND  MODIFIED_TM <= '2017-03-13 21:09:43'
              AND  POST_ID < 0
    ) inner_t
    where  true
    ORDER BY  MODIFIED_TM DESC
    LIMIT  1000\G;
*************************** 1. row ***************************
           id: 1
  select_type: PRIMARY
        table: <derived2>
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 4950
        Extra: Using filesort
*************************** 2. row ***************************
           id: 2
  select_type: DERIVED
        table: OTHERS_TINY_URL_TBL
         type: range
possible_keys: XIE2_OTHERS_TINY_URL_TBL,XIE5_OTHERS_TINY_URL_TBL
          key: XIE2_OTHERS_TINY_URL_TBL
      key_len: 9
          ref: NULL
         rows: 4950
        Extra: Using index condition; Using where
2 rows in set (0.00 sec)

ERROR: 
No query specified

为什么第一个查询效率低下?

1 个答案:

答案 0 :(得分:2)

首先,重要的是要注意rows中的数字只是一个估计,基于一些统计数据,并且根据您的MySQL版本,在规划阶段对您的索引进行一些随机查找。有时它可能完全错误(并且基于这些估计,MySQL可能会选择较慢的执行计划)。这就是为什么,如果你对MySQL没有的数据有所了解,可以通过例如优化查询来优化查询。强制MySQL不想选择的索引。

此外,MySQL尝试最小化的唯一相关值是总执行时间。它可能与它必须读取的行数相关联,但不必(例如,如果您不必执行文件排序)。即使它通常可能选择一个好的策略,它可能取决于您的实际数据,如果它实际上是最快的。尝试例如limit 10模拟而不是85,4000行将满足您的搜索条件。这完全取决于您的数据,而不是查询本身 - 并且MySQL必须在执行查询之前决定如何执行查询。

但一般情况下,由于您的查询使用的是不同的索引,因此预计您的查询在rows中会有不同的数字。

第一个查询将使用MODIFIED_TM上的索引,该索引遍历给定日期范围内的所有行。估计是47168行位于此范围内。对于所有这些行,它将检查其他列以查找其他条件。

您的第二个查询将使用不同列上的索引,可能是LINK_TYPE。您必须添加索引定义以使其清晰,并且可以例如也是URL_SHORTNER_ID,但对于以下内容,我假设索引是(仅)LINK_TYPE。估计是4950行具有此LINK_TYPE。这实际上往往是一个糟糕的估计,你可以通过例如检查正确的数字。 SELECT count(*) FROM OTHERS_TINY_URL_TBL WHERE LINK_TYPE = 'BITLY'。如果这些行与其他条件匹配,则必须检查这些行中的每一行。

MySQL还不知道最终只能找到85行,并且检查4950行和订购剩余的85行可能比检查已经按正确顺序排列的47168行更快(和所以不必事后订购,这是MySQL通常试图阻止的相对较慢的操作)。您也可能会感到幸运:可能会发生这些47168行中的前1000行必须纠正link_typeurl_shortener_idpost_id,因此它必须只检查1000行而不是检查4950行并进行排序。但这仅取决于您的数据。如果你对MySQL没有的数据有所了解,你应该使用优化器提示,例如:强制使用不同的索引。或者欺骗MySQL,例如通过使用你的第三个查询 - 虽然MySQL 5.7可能会诱骗你,因为它实际上应该优化子查询。

幸运的是,有一个更好的解决方案。您的第一个查询有一个完美的索引(这也会同时改善您的第二个查询):OTHERS_TINY_URL_TBL(URL_SHORTNER_ID, LINK_TYPE, MODIFIED_TM)