Mysql 奇怪的行为:在where条件下扫描的列中没有索引的行更少

Mysql 奇怪的行为:在where条件下扫描的列中没有索引的行更少,mysql,sql,indexing,Mysql,Sql,Indexing,我有一个名为a的表,它存储经度和纬度数据。还有一个查询,用于获取位置在某个地理矩形内的行,例如: SELECT * FROM A WHERE A.longitude > -121.276052 AND A.longitude < -110.159143 AND A.latitude > 32.802275 AND A.latitude < 38.335916 ORDER BY A.id LIMIT 100 表A在id列上只有索引,id列也是主键。

我有一个名为a的表,它存储经度和纬度数据。还有一个查询,用于获取位置在某个地理矩形内的行,例如:

SELECT *
FROM A
WHERE A.longitude > -121.276052
    AND A.longitude < -110.159143
    AND A.latitude > 32.802275
    AND A.latitude < 38.335916
ORDER BY A.id
LIMIT 100
表A在id列上只有索引,id列也是主键。我希望扫描大量的行,因为在经度和纬度列上没有索引。然而,它只扫描explain输出中的100行

我还验证了输出行的ID不是偶然最小的。它们实际上在ID范围

的中间。
为什么mysql只能用id索引准确地识别100行?

我不能100%确定问题是什么。在您的查询中,MySQL正在扫描order by的索引,它将按id顺序获取所有行。然后,它将查找数据页中的每一行,以查看它是否匹配where子句


据推测,索引中的前100行与where子句匹配。因此,MySQL可以在100行之后停止扫描。

我不能100%确定问题是什么。在您的查询中,MySQL正在扫描order by的索引,它将按id顺序获取所有行。然后,它将查找数据页中的每一行,以查看它是否匹配where子句

据推测,索引中的前100行与where子句匹配。因此,MySQL可以在100行之后停止扫描。

简短的回答是:EXPLAIN的行不可信

答案很长:

可以信任以下内容:

FLUSH STATUS;
SELECT ...;
SHOW SESSION STATUS LIKE 'Handler%';
如果你看到100次阅读,或者99次或101次,那么这是很好和有效的。但我怀疑您将看到一个更大的数字,但少于表中的行数。但是,如果该边界框中的行少于100行,则必须扫描整个表

部分加速

会有帮助的。这对于中型lat lng表来说已经足够好了。这应该是一个数量级的速度比你有

延迟获取可能会有更多帮助:

SELECT *
    FROM A
    JOIN ( SELECT id FROM A WHERE lat... AND lng... ) AS x USING(id)
-- and have this "covering" index for the subquery:
INDEX(latitude, longitude, id)
进一步加速

如果您有一个大表,那么解决方案会变得更复杂;我讨论如何使用。它包含的存储例程速度快了一个数量级。

简短的回答是:EXPLAIN的行不可信

答案很长:

可以信任以下内容:

FLUSH STATUS;
SELECT ...;
SHOW SESSION STATUS LIKE 'Handler%';
如果你看到100次阅读,或者99次或101次,那么这是很好和有效的。但我怀疑您将看到一个更大的数字,但少于表中的行数。但是,如果该边界框中的行少于100行,则必须扫描整个表

部分加速

会有帮助的。这对于中型lat lng表来说已经足够好了。这应该是一个数量级的速度比你有

延迟获取可能会有更多帮助:

SELECT *
    FROM A
    JOIN ( SELECT id FROM A WHERE lat... AND lng... ) AS x USING(id)
-- and have this "covering" index for the subquery:
INDEX(latitude, longitude, id)
进一步加速


如果您有一个大表,那么解决方案会变得更复杂;我讨论如何使用。它包含的存储例程速度快了一个数量级。

在这种情况下使用BETWEEN子句是值得的。@tadman ya,但我认为这不会有任何区别。我想说的是,为什么这个查询只扫描100行id索引。前100行恰好与WHERE子句中的条件匹配,所以MySQL停止扫描。@GordonLinoff nope。我对你的答案发表了评论。解释输出中的行是一个估计值。也许优化器估计,根据限制,它只需要查看100行。可能优化器没有很好地估计谓词的选择性,因为没有索引统计数据。优化器乐观地认为,它查看的前100行将满足谓词。如果谓词是高度选择性的,dba会创建一个索引。在这种情况下,使用BETWEEN子句是值得的。@tadman ya,但我认为这不会有任何区别。我想说的是,为什么这个查询只扫描100行id索引。前100行恰好与WHERE子句中的条件匹配,所以MySQL停止扫描。@GordonLinoff nope。我对你的答案发表了评论。解释输出中的行是一个估计值。也许优化器估计,根据限制,它只需要查看100行。可能优化器没有很好地估计谓词的选择性,因为没有索引统计数据。优化器乐观地认为,它查看的前100行将满足谓词。如果谓词是高度选择性的,dba会创建index.ya,这也是我的第一个猜测。我在问题中也提到。我验证了返回行的ID。它们不是最小的。是的,这也是我的第一个猜测。我在问题中也提到。我验证了返回行的ID。它们不是最小的。在答案中。在答案中。