MySQL "EXPLAIN" 语句显示非最左 where 条件使用不正确索引的查询
MySQL The "EXPLAIN" statement shows the query with non-left-most where condition uses improper index
假设我们只有一个索引 idx_abcd,即 (A, B, C, D)。大约有 3000 万行。
我们现在有一个查询
SELECT COUNT(*)
FROM tableName
WHERE B = ? AND C = ? AND D = ?;
上述查询的EXPLAIN
语句显示了以下有用数据:
- 类型:索引
- 键:idx_abcd
- 行数:~3000 万
- ref: 空
- 过滤:0.1
- 额外:使用哪里;使用索引
WHERE
条件不遵循最左原则,但EXPLAIN
说明DB确实使用了索引
有可能吗?
MySQL的版本是5.7。引擎是 InnoDB。
这里的一个可能的解释是 MySQL 发现扫描( 而不是 查找)idx_abcd
索引,无论出于何种原因,都比仅仅扫描更快扫描 table 的聚集索引。为此,它选择使用索引。请注意,正如您所描述的那样,“最左原则”并没有发生在这里。也就是说,因为您的索引以 A
列开头,所以它对 WHERE
子句没有真正的帮助,该子句允许 A
.
的任何值
EXPLAIN 字段 type: index
表明它正在进行“索引扫描”。换句话说,它遍历索引中的每个条目。这也相当昂贵,可能几乎与 table-扫描一样昂贵。
但是如果 table 中除了 B、C、D 之外还有其他列,优化器可能会估计读取该索引的每一页比读取每个索引的开销至少要低一些整个页面 table.
EXPLAIN 输出有点令人困惑,因为它确实告诉您它正在使用索引,甚至在额外的注释字段中显示“使用索引”。我们被告知这个注释意味着查询能够通过 仅 读取该索引来获得它需要的东西,并且它可以避免读取 table 本身的页面。在这种情况下确实如此。它只读取指定的索引。但它正在读取 整个 索引,而不仅仅是符合您条件的索引子集。
您可能已经知道为什么它不能使用您的索引 idx_abcd
进行行限制。因为索引还有一列A
作为最左边的列,所以不能假设符合你条件的索引条目都存储在一起。
假设是用INDEX(a,b,c,d)
,比扫描整个table好,但不如用
INDEX(b,c,d) -- in any order
对于 (a,b,c,d),它需要读取没有匹配 b,c,d 的行,然后不计数。
对于 (b,c,d) 或 (b,c,d,a),所有要计数的行在索引中都是相邻的。此外,没有不需要的行需要跳过。
select列是count(*),where列被index_abcd包含,所以不需要返回table去取其他数据。
关键字:覆盖索引
假设我们只有一个索引 idx_abcd,即 (A, B, C, D)。大约有 3000 万行。
我们现在有一个查询
SELECT COUNT(*)
FROM tableName
WHERE B = ? AND C = ? AND D = ?;
上述查询的EXPLAIN
语句显示了以下有用数据:
- 类型:索引
- 键:idx_abcd
- 行数:~3000 万
- ref: 空
- 过滤:0.1
- 额外:使用哪里;使用索引
WHERE
条件不遵循最左原则,但EXPLAIN
说明DB确实使用了索引
有可能吗?
MySQL的版本是5.7。引擎是 InnoDB。
这里的一个可能的解释是 MySQL 发现扫描( 而不是 查找)idx_abcd
索引,无论出于何种原因,都比仅仅扫描更快扫描 table 的聚集索引。为此,它选择使用索引。请注意,正如您所描述的那样,“最左原则”并没有发生在这里。也就是说,因为您的索引以 A
列开头,所以它对 WHERE
子句没有真正的帮助,该子句允许 A
.
EXPLAIN 字段 type: index
表明它正在进行“索引扫描”。换句话说,它遍历索引中的每个条目。这也相当昂贵,可能几乎与 table-扫描一样昂贵。
但是如果 table 中除了 B、C、D 之外还有其他列,优化器可能会估计读取该索引的每一页比读取每个索引的开销至少要低一些整个页面 table.
EXPLAIN 输出有点令人困惑,因为它确实告诉您它正在使用索引,甚至在额外的注释字段中显示“使用索引”。我们被告知这个注释意味着查询能够通过 仅 读取该索引来获得它需要的东西,并且它可以避免读取 table 本身的页面。在这种情况下确实如此。它只读取指定的索引。但它正在读取 整个 索引,而不仅仅是符合您条件的索引子集。
您可能已经知道为什么它不能使用您的索引 idx_abcd
进行行限制。因为索引还有一列A
作为最左边的列,所以不能假设符合你条件的索引条目都存储在一起。
假设是用INDEX(a,b,c,d)
,比扫描整个table好,但不如用
INDEX(b,c,d) -- in any order
对于 (a,b,c,d),它需要读取没有匹配 b,c,d 的行,然后不计数。
对于 (b,c,d) 或 (b,c,d,a),所有要计数的行在索引中都是相邻的。此外,没有不需要的行需要跳过。
select列是count(*),where列被index_abcd包含,所以不需要返回table去取其他数据。
关键字:覆盖索引