为什么 mysql 更喜欢扫描 table 而不是使用复合索引?
why mysql prefer to scan the table rather than using composite index?
我有这个 table :
企业:
bussId | x | y | bussStatus |
我想运行这个查询:
SELECT * FROM bussiness WHERE (bussiness.x BETWEEN 31.214857 AND 31.658529) AND (bussiness.y BETWEEN 34.502798 AND 34.918799) AND bussId > 500 AND bussStatus >0
EXPLAIN 查询 return 这个结果:
Id : 1
select_type : SIMPLE
table : bussiness
type : range
possible_keys : PRIMARY,bussStatus,xy,bussId_xy_status
key : PRIMARY
key_len : 4
ref : NULL
rows : 134680
Extra : Using where
这表明 mysql 选择使用 PRIMARY
键,尽管有一个复合索引 bussId_xy_status
可以对 x 、 y 、 status 和 bussId 进行排序!
这是您的查询:
SELECT b.*
FROM business b
WHERE (b.x BETWEEN 31.214857 AND 31.658529) AND
(b.y BETWEEN 34.502798 AND 34.918799) AND
b.bussId > 500 AND
b.bussStatus > 0;
WHERE
子句中的所有比较都是不等式。这意味着复合索引的用途非常有限——只有索引中的第一列可以最有效地使用。优化器已决定使用主键的完整 table 扫描最有效,这可能是因为 bussid
上的条件。 MySQL documentation 很好地回顾了复合索引及其使用方法。
如果您需要进行这些类型的地理比较,您可以考虑使用空间索引——参见 here。
编辑:
无赖。我引用的 MySQL 文档在解释多列索引方面做得不如应有的好。索引基本上提供两种访问功能:索引查找和索引扫描。查找允许索引转到正确的值集。索引扫描允许索引识别两个值之间的所有行。 (最常见的索引类型是支持这两种操作的 B 树索引。)
考虑 table t 的索引在 (a, b, c)
上。当所有比较都由 AND 连接并且至少有一个在 a
上时,索引可用于 where
子句。因此,该索引可用于:
where a = 'xyz'
where a > 'xyz'
where a in ('xyz', 'tbd')
首先是平等。后面两个是不等式,因为单次索引查找是不够的。
索引不能(通常)用于:
where b = 'xyz'
where a = 'xyz' or b = 'xyz'
引入第二列时会发生什么?
where a = 'xyz' and b = 'abc'
where a > 'xyz' and b = 'abc'
where a in ('xyz', 'tbd') and b = 'abc'
第一种情况可以充分利用索引。对于后两个,仅使用索引的 "a" 部分(如果有的话)。优化器可能决定根本不使用索引,因为第一部分选择了太多行。
一般规则可以描述如下。多列索引可用于具有以下条件的 where
子句:
where
子句中的所有比较都由 AND 连接。
- 对于索引中的第一个"n"键("left prefix"),比较是相等的(即
=
或IS NULL
)。
- n + 1 键允许任何比较
- "n" 可以等于零
索引无法帮助 "n + 1" 键之后的任何后续比较。它可以通过另一种方式提供帮助,即作为覆盖索引,但这是一个单独的主题。
在你的例子中,所有的比较都是不等式,所以 "n" 是 0。只有索引中的第一个键可以被有效地使用,并且优化器已经决定这没有足够的选择性来提高效率。
另请注意,只要列上有函数,就不会使用索引。这在您的情况下不是问题,而是一些简单的事情:a + 0 = 0
can 阻止使用索引(我认为这个特定示例在不同的数据库中可能有不同的工作方式) .
我有这个 table :
企业:
bussId | x | y | bussStatus |
我想运行这个查询:
SELECT * FROM bussiness WHERE (bussiness.x BETWEEN 31.214857 AND 31.658529) AND (bussiness.y BETWEEN 34.502798 AND 34.918799) AND bussId > 500 AND bussStatus >0
EXPLAIN 查询 return 这个结果:
Id : 1
select_type : SIMPLE
table : bussiness
type : range
possible_keys : PRIMARY,bussStatus,xy,bussId_xy_status
key : PRIMARY
key_len : 4
ref : NULL
rows : 134680
Extra : Using where
这表明 mysql 选择使用 PRIMARY
键,尽管有一个复合索引 bussId_xy_status
可以对 x 、 y 、 status 和 bussId 进行排序!
这是您的查询:
SELECT b.*
FROM business b
WHERE (b.x BETWEEN 31.214857 AND 31.658529) AND
(b.y BETWEEN 34.502798 AND 34.918799) AND
b.bussId > 500 AND
b.bussStatus > 0;
WHERE
子句中的所有比较都是不等式。这意味着复合索引的用途非常有限——只有索引中的第一列可以最有效地使用。优化器已决定使用主键的完整 table 扫描最有效,这可能是因为 bussid
上的条件。 MySQL documentation 很好地回顾了复合索引及其使用方法。
如果您需要进行这些类型的地理比较,您可以考虑使用空间索引——参见 here。
编辑:
无赖。我引用的 MySQL 文档在解释多列索引方面做得不如应有的好。索引基本上提供两种访问功能:索引查找和索引扫描。查找允许索引转到正确的值集。索引扫描允许索引识别两个值之间的所有行。 (最常见的索引类型是支持这两种操作的 B 树索引。)
考虑 table t 的索引在 (a, b, c)
上。当所有比较都由 AND 连接并且至少有一个在 a
上时,索引可用于 where
子句。因此,该索引可用于:
where a = 'xyz'
where a > 'xyz'
where a in ('xyz', 'tbd')
首先是平等。后面两个是不等式,因为单次索引查找是不够的。
索引不能(通常)用于:
where b = 'xyz'
where a = 'xyz' or b = 'xyz'
引入第二列时会发生什么?
where a = 'xyz' and b = 'abc'
where a > 'xyz' and b = 'abc'
where a in ('xyz', 'tbd') and b = 'abc'
第一种情况可以充分利用索引。对于后两个,仅使用索引的 "a" 部分(如果有的话)。优化器可能决定根本不使用索引,因为第一部分选择了太多行。
一般规则可以描述如下。多列索引可用于具有以下条件的 where
子句:
where
子句中的所有比较都由 AND 连接。- 对于索引中的第一个"n"键("left prefix"),比较是相等的(即
=
或IS NULL
)。 - n + 1 键允许任何比较
- "n" 可以等于零
索引无法帮助 "n + 1" 键之后的任何后续比较。它可以通过另一种方式提供帮助,即作为覆盖索引,但这是一个单独的主题。
在你的例子中,所有的比较都是不等式,所以 "n" 是 0。只有索引中的第一个键可以被有效地使用,并且优化器已经决定这没有足够的选择性来提高效率。
另请注意,只要列上有函数,就不会使用索引。这在您的情况下不是问题,而是一些简单的事情:a + 0 = 0
can 阻止使用索引(我认为这个特定示例在不同的数据库中可能有不同的工作方式) .