我们最近继承了一个基于PHP的网站代码库,并且有一个SQL查询使得 mysqld 进程在运行时没有响应约50秒(并在{{1}上占用100%的CPU })。它涉及通过比较其邮政编码的地理坐标来确定哪些位置在给定半径内。坦率地说,我不能为什么它如此沉重地做出正面或反面。我认为可能是大量使用trig和sqrt(),但使用不同的公式几乎没有效果。 (作为奖励,它甚至没有用。)
top
表有~45k条目,但据我所知,其他查询都没有这么长。实际上,基于名称的搜索(与上面的地理邻近搜索相反)实际上是在同一数据集上瞬时完成的。我对SQL不是很熟悉,有人可以帮我找出导致瓶颈的原因吗?
我应该注意到,在给我们代码库之前,它在以前的家中运行得非常好。
salon_locations
答案 0 :(得分:3)
使用PHP根据给定的半径向SQL查询添加“边界框”。请参阅我对this question的回复,了解其工作原理。
修改强>
基本上,您根据半径预先计算最大和最小经度和纬度,然后将其添加到SQL查询中
AND salon_locations.latitude != ''
AND salon_locations.latitude BETWEEN $minLatitude and $maxLatitude
AND salon_locations.longitude != ''
AND salon_locations.longitude BETWEEN $minLongitude and $maxLongitude).
这会将SQL select限制为沙龙的子集;并且您的距离计算仅针对该子集而不是您当前正在计算的大集执行。
答案 1 :(得分:1)
攻击这些查询的最佳方法是使用所寻求的最大距离/纬度/长度*半径预过滤纬度范围。它给出了一个边界为40%的边界框,但它是一个快速的预滤镜,很容易应用于纬度指数,而无需完全计算每个点与原点的距离。
答案 2 :(得分:1)
如果您的表格为MyISAM
,我建议使用Point
数据类型存储坐标,在其上创建空间索引并在查询中使用它:
SELECT *,
FROM salon_locations sl
JOIN …
WHERE MBRContains
(
LineString
(
Point($northing - $radius, $easting - $radius),
Point($northing + $radius, $easting + $radius)
),
sl.location
)
请注意,最好使用UTM
坐标(metrical easting and northing)而不是lat
和lon
来简化计算。不幸的是,yuo只能在一个区域内使用它们,因为MySQL
不允许创建混合相等和空间索引,但是,如果所有对象都在一个半球内并且不是非常靠近两极,那么你可以使用你的拥有错误的东西和北方,这将给你足够小的半径(小于500公里或像这样)的结果。