为啥排序行会减少解释中的行数?

Posted

技术标签:

【中文标题】为啥排序行会减少解释中的行数?【英文标题】:Why does ordering rows lessen the row count in an explain?为什么排序行会减少解释中的行数? 【发布时间】:2015-11-19 13:23:21 【问题描述】:

我有一张大约有两三百万行的表格……

mysql> select count(*) from tbl;
+----------+
| count(*) |
+----------+
|  2615889 |
+----------+
1 row in set (1.23 sec)

mysql> show indexes from tbl;
+-------+------------+-------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name          | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+-------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| tbl   |          0 | PRIMARY           |            1 | tbl_id      | A         |     2284627 |     NULL | NULL   |      | BTREE      |         |               |
| ...
| tbl   |          1 | tbl_fld           |            1 | fld         | A         |     2284627 |     NULL | NULL   | YES  | BTREE      |         |               |
+-------+------------+-------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
6 rows in set (0.30 sec)

...对于以下查询,如果我添加 order by 子句,我似乎做得更好(即,我最终使用索引)...

mysql> explain select * from tbl
    -> where fld in (select fld from tbl group by fld having count(*)>1)
    -> limit 1000;
+----+--------------------+-------+-------+---------------+---------+---------+------+---------+-------------+
| id | select_type        | table | type  | possible_keys | key     | key_len | ref  | rows    | Extra       |
+----+--------------------+-------+-------+---------------+---------+---------+------+---------+-------------+
|  1 | PRIMARY            | tbl   | ALL   | NULL          | NULL    | NULL    | NULL | 2328333 | Using where |
|  2 | DEPENDENT SUBQUERY | tbl   | index | NULL          | tbl_fld | 15      | NULL |       1 | Using index |
+----+--------------------+-------+-------+---------------+---------+---------+------+---------+-------------+
2 rows in set (0.00 sec)

mysql> explain select * from tbl
    -> where fld in (select fld from tbl group by fld having count(*)>1)
    -> order by fld limit 1000;
+----+--------------------+-------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type        | table | type  | possible_keys | key     | key_len | ref  | rows | Extra       |
+----+--------------------+-------+-------+---------------+---------+---------+------+------+-------------+
|  1 | PRIMARY            | tbl   | index | NULL          | tbl_fld | 15      | NULL | 1000 | Using where |
|  2 | DEPENDENT SUBQUERY | tbl   | index | NULL          | tbl_fld | 15      | NULL |    1 | Using index |
+----+--------------------+-------+-------+---------------+---------+---------+------+------+-------------+
2 rows in set (0.00 sec)

……这是为什么呢?

【问题讨论】:

您是运行查询还是只是比较他们的计划?我认为 MySQL 不会在您的第一个查询中真正扫描整个表。 LIMIT 会使其更快。 @Tim3880,只是explain 【参考方案1】:

LIMIT 不应该在没有 ORDER BY 的情况下使用,因为没有明确定义将返回哪些行。正如您发现 ORDER BY .. LIMIT optimization 不起作用(根本没有实现 AFAIK),而当您添加 ORDER BY 时,优化开始并且 MySQL 在找到足够的行后停止执行。

【讨论】:

我在您链接到的页面上看不到任何相关内容;您指的是该页面的哪一部分? 你是对的,该页面没有涵盖这种确切情况,而不是“一旦 MySQL 向客户端发送了所需的行数,它就会中止查询”——它说查询应该同样快,在某些情况下,不使用索引的查询可能更快。 MySQL 无法确定哪些行可能与子查询匹配(因为它被评估为 DEPENDENT),因此它在不需要排序时更喜欢全表扫描 - 因为快速找到 1000 行的概率是相同的,并且表扫描是通常最适合磁盘 IO(主要是顺序扫描)。 如果您将子查询重写为具体化/派生表而不是依赖表,一切都可能会改变。这样就可以将子查询作为仅索引扫描进行,然后直接通过该索引选择相关的 1000 行。【参考方案2】:

乍一看,它似乎在第二个查询中使用了一个键,这是一件好事,因为它可以防止对表进行完整扫描。

【讨论】:

我在问题中提到了这一点。问题是为什么它计划在第二个而不是第一个查询中使用索引。

以上是关于为啥排序行会减少解释中的行数?的主要内容,如果未能解决你的问题,请参考以下文章

为啥“解释”返回的行不等于count()?

为啥“解释”返回的行不等于count()?

计算 30 天 bin 中的行数

为啥这个函数不并行?

尝试使用 reloadsections 重新加载时,节中的行数无效

为啥选择单个属性返回的行数少于选择 Oracle SQL 中的所有列