index 用于 SELECT COUNT(1) 但不用于 SELECT *

问题描述 投票:0回答:1

这是我的查询:

SELECT COUNT(1)
FROM nhd
WHERE gnis_id IN (01372336);

表定义上有一个

INDEX gnis_id (gnis_id)
,实际上,该索引用于此查询。但是当我用
COUNT(1)
替换
*
时,索引是not使用,至少根据
EXPLAIN
。这是为什么?为什么更改我返回的列会改变 MySQL 决定将哪个索引用于查询的 WHERE 部分的方式?

这是

*
的 EXPLAIN 的输出:

id: 1
select_type: SIMPLE
table: nhd
partitions: NULL
type: ALL
possible_keys: gnis_id
key: NULL
key_len: NULL
ref: NULL
rows: 2752840
filtered: 10.00
Extra: Using where

这是

SHOW INDEX FROM nhd
的输出:

https://pastebin.com/KXpqBeJm

我正在运行 MySQL 8.0.33

sql mysql indexing query-optimization explain
1个回答
0
投票

从你的

SHOW INDEX
语句的结果,我们可以看到关于表条件的信息
gnis_id

Table: nhd
Non_unique: 1
Key_name: gnis_id
Seq_in_index: 1
Column_name: gnis_id
Collation: A
Cardinality: 16206
Sub_part: NULL
Packed: NULL
Null:
Index_type: BTREE
Comment:
Index_comment:
Visible: YES
Expression: NULL

正如基数所示,索引的选择性即基数/表行计数 16206 / 2752840 = 0.0059 非常低。并且通过使用

select *
,它的项目超出了索引的存储值,如果 MySQL 决定使用索引,它必须使用每个索引行末尾的聚集键来引用主表。
由于上面提到的两个问题:低选择性和必须使用聚簇索引来引用主表以查找丢失的信息,MySQL 的优化器认为最好使用 FULL TABLE 扫描(基于聚簇索引)代替,因为它发现后者在(全表扫描)和(额外的参考工作加上低选择性)之间的权衡中更糟。
您可以尝试以下几件事:

首先,使用

select gnis_id
而不是
select *
,这将使 MySQL 使用索引,因为索引覆盖了 gnis_id 本身的值,这意味着不需要额外的参考工作。您会发现执行计划中的访问类型为
ref
,这使得查询响应时间明显优于由
ALL
.
引起的访问类型
select *
其次,使用
select *
子句但在from子句的末尾添加
force index(gnis_id)
强制MySQL使用索引。

SELECT *
FROM nhd force index(gnis_id)
WHERE gnis_id IN (01372336);

找出执行它需要多长时间,并比较在不强制索引的情况下执行它需要多长时间。你会明白为什么 MySQL 决定不使用索引。

© www.soinside.com 2019 - 2024. All rights reserved.