我有一个结构如下的 map 表:
CREATE TABLE `map` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`occupied` tinyint(2) NOT NULL DEFAULT '0',
`c_type` tinyint(4) NOT NULL DEFAULT '0',
`x` int(11) NOT NULL,
`y` int(11) NOT NULL,
`terrain` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `get_map_w_radius` (`x`,`y`,`id`,`terrain`,`occupied`,`c_type`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4_general_ci
有 40k 条记录,x 和 y 都在 1 到 200 之间。
在我的脚本中我这样使用它:
SELECT id, terrain, occupied, c_type FROM map WHERE x >= $x-$radius AND x <= $x+$radius AND y >= $y-$radius AND y <= $y+$radius LIMIT 30
例如 $x 为 15,y 为 95,半径为 5。 当我分析查询时,发送数据是 0.000496 毫秒,但如果不覆盖索引(仅 x 和 y),它运行得更快,即使理论上它应该是其他方式? 使用覆盖索引,当我使用简单的 where 子句仅使用 x 和 y 一次进行选择查询时:
SELECT id, terrain, c_type, occupied FROM map WHERE x >= $x And y <= $y limit 30;
它的执行速度更快,仅在 0.000059 内发送数据。 我有什么遗漏或误解吗?也许事情应该是这样的?
最佳答案
对于仅有 40K 行,建议添加
INDEX(x),
INDEX(y)
这样,优化器就可以查看 BETWEENs
然后选择一个可能效果更好的方法并减少工作量一些。
进一步的优化很棘手。它们在标记为[纬度-经度]的问题中反复讨论。
(术语争论)“半径”意味着二维“距离”。你拥有的是一个“边界框”。
没有ORDER BY
,查询将返回任何 30
行,不一定是最接近的 30 行。如果您对此感到满意,那很好,因为它更快。
“覆盖指数”与 INDEX(x)
-- 我有一个规则:不要创建超过 5 列的索引。这样做并没有什么错,只是它会变得笨重。我的建议还有INDEX(y)
基于 y
的假设有时是更好的过滤器。
当心查询缓存——如果它被打开,你的“更快”运行可能会因此而发生。使用 SELECT SQL_NO_CACHE ...
运行您的计时进行诚实的比较。
您的 6 列索引是唯一的吗?如果是这样,就把它作为PK,彻底摆脱id
.
如果x
和y
总是 0..200,然后将它们设为 TINYINT UNSIGNED
(范围为 0..255,1 字节而不是 4)。
“范围”的所有风格( BETWEEN
、 <=
- 2 面或 1 面)执行相同。因此,任何性能差异都是其他因素造成的......
- 向下钻取 BTree 到起始值(可能是表的开头)
- 向前扫描
- 如果
LIMIT
则停止到达并且没有ORDER BY
- 停在结束值(或表格末尾)
关于php - Mysql,复杂的Where子句会减慢查询速度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42657422/