使用PHP/Laravel从MySQL/MariaDB获取所有poi,哪种方法更快
如果我错了,请纠正我 用户在我的网站上创建了三种获取最近家的方法:使用PHP/Laravel从MySQL/MariaDB获取所有poi,哪种方法更快,mysql,laravel,mariadb,spatial,point-of-interest,Mysql,Laravel,Mariadb,Spatial,Point Of Interest,如果我错了,请纠正我 用户在我的网站上创建了三种获取最近家的方法: 要创建一个包含两列(纬度、经度)且两列都是浮动的表,请执行以下操作: 这是: $latitude = 50; $longitude = 60; SELECT * FROM my_table WHERE (latitude <= $latitude+10 AND latitude >= $latitude-10) AND (longitude <= $longitude+10 AND
$latitude = 50;
$longitude = 60;
SELECT * FROM my_table
WHERE (latitude <= $latitude+10 AND latitude >= $latitude-10)
AND (longitude <= $longitude+10 AND longitude >= $longitude-10)
$latitude=50;
$longitude=60;
从my_表中选择*
其中(纬度=$latitude-10)
和(经度=经度$10)
例如,这里的10表示1公里
在这种方法中,我们也可以使用harvesine公式
谢谢大家包围盒和哈弗森 在简短的
选择中,您使用的是“边界框”方法,即在地图上绘制一个粗略的正方形。然而,它有几个缺陷
- 50和60大概是以度为单位的;你说10是以公里为单位的。你不能在不转换其中一个的情况下混合它们
- 经度比纬度短;需要一个
cos()
来修复此问题
有了这些帮助,边界框可以显著地过滤行,然后可选的haversine测试可以绕过测试的范围
INDEX(latitude)
INDEX(longitude)
这种方法具有“中等”性能——其中一个索引将与边界框一起使用,从而快速将候选对象限制为全球范围内的东西(或南北)条带。但这可能仍然有很多候选人
通过过滤掉大部分行,Haversine调用的数量并不太糟糕;不要担心函数的性能
如果您有一百万个家庭,那么包含5个家庭(加上一些未通过haversine检查的家庭)的最终边界框可能会涉及几千行,因为只使用了两个索引中的一个。这仍然比获取所有一百万行并使用距离函数检查每一行要好得多
点和空间索引
切换到点
需要切换到空间
索引。在此模式下,ST_Distance_Sphere()
可代替哈弗森线。(注意:该功能仅在最新版本中存在。)
通过过滤掉大部分行,调用ST_Distance
或ST_Distance\u Sphere
的次数并不太多;不要担心函数的性能
SPATIAL
搜索使用R-树。在你的询问中,我对他们的表现感觉不好
方法3
通过从另一个点分类开始,可以增加复杂性。您还需要检查相邻区域以查看附近是否有点。没有更多细节,我无法判断相对表现
我的方法
我有一些复杂的代码,可以扩展到任意多个点。因为您的数据集可能足够小,可以缓存在RAM中,所以对您来说可能有些过分
对于只有一百万个家庭来说,上面的两个索引可能“足够好”,因此您不需要求助于“我的算法”。我的算法只需触摸大约20行就可以得到所需的5行——不管行的总数是多少
其他注释
如果同时存储lat/lng和点
,则表格将变得笨重;如果尝试混合使用边界框和ST
函数,请记住这一点。使用哪个公式计算距离并不重要。更重要的是您必须读取、处理和排序的行数。在最好的情况下,您可以在WHERE子句中使用条件索引来限制处理的行数。您可以尝试对您的位置进行分类,但这取决于数据的性质,如果这样做效果好的话。您还需要找出要使用的“类别”。一个更通用的解决方案是使用空间索引和ST_in()函数
现在让我们运行一些测试
在my DB(MySQL)中
CREATE TABLE `cities` (
`cityId` MEDIUMINT(9) UNSIGNED NOT NULL AUTO_INCREMENT,
`country` CHAR(2) NOT NULL COLLATE 'utf8mb4_unicode_ci',
`city` VARCHAR(100) NOT NULL COLLATE 'utf8mb4_unicode_ci',
`accentCity` VARCHAR(100) NOT NULL COLLATE 'utf8mb4_unicode_ci',
`region` CHAR(2) NULL DEFAULT NULL COLLATE 'utf8mb4_unicode_ci',
`population` INT(10) UNSIGNED NULL DEFAULT NULL,
`latitude` DECIMAL(10,7) NOT NULL,
`longitude` DECIMAL(10,7) NOT NULL,
`geoPoint` POINT NOT NULL,
PRIMARY KEY (`cityId`),
SPATIAL INDEX `geoPoint` (`geoPoint`)
) COLLATE='utf8mb4_unicode_ci' ENGINE=InnoDB
set @lon = 0.0;
set @lat = 51.5;
select c.cityId, c.accentCity, st_distance_sphere(c.geoPoint, point(@lon, @lat)) as dist
from cities c
order by dist
limit 1
988204 Blackwall 1085.8212159861014
set @point = point(@lon, @lat);
set @radius = 0.1;
set @polygon = ST_Buffer(@point, @radius);
select c.cityId, c.accentCity, st_distance_sphere(c.geoPoint, point(@lon, @lat)) as dist
from cities c
where st_within(c.geoPoint, @polygon)
order by dist
limit 1