mysql 扫描行数差异

标签 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

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

最佳答案

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

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

但一般来说,您的查询预计会有不同的行数,因为您的查询使用不同的索引。

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

您的第二个查询将使用不同列上的索引,可能是LINK_TYPE。您必须添加索引定义以使其清晰,并且它可以例如也是 URL_SHORTNER_ID,但对于以下内容,我假设索引(仅)位于 LINK_TYPE 上。估计有 4950 行具有此 LINK_TYPE。实际上,这通常是一个错误的估计,您可以通过例如以下方式检查正确的数字: 从 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)

关于mysql 扫描行数差异,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43063333/

相关文章:

php - MySQL - 完整行检索与部分行检索

php - 从 NSString 到 PHP 到 MySql 时如何处理回车?

mysql - SUM(val/2) 和 SUM(val)/2 哪个更好?

mysql - 如何加快MySQL查询

mySQL 将 ASCII 排序为 INT

mysql 多锁,单行

mysql - 当解释看起来不错时如何改善查询时间?

sql - 一个存储过程怎么可以有多个执行计划呢?

sql-server - 存储过程中查询的不同执行计数

sql - 解释计划在应用程序之间有何不同