*** MYSQL 算法难题: 查询距离指定坐标 10 公里范围内的所有店铺 ***

253 天前
 Angela2022
我有 MYSQL 表含 20 万条记录, 每条记录有店铺和位置经纬度字段. 现在我用 sql 查询距离指定坐标半径 10 公里内的所有店铺, 发现查询速度奇慢, 做了 index 后也是如此.

问题:
1. 上述需求,用啥数据格式的字段存位置经纬度合适?
2. 求最新最快的半径 10 公里内的所有店铺查询算法, 最好支持 MYSQL.

谢谢
12624 次点击
所在节点    程序员
107 条回复
SimbaPeng
252 天前
也不知道从哪听了个 spark ,见人都要叭叭两句,笑死我了
flyz
252 天前
@249239432 相当于跑出来存表?每次查询都做表查询么。
249239432
252 天前
@flyz 数据从表里面加载,在 spark 计算,结果你要存表或者干什么都行
ffw5b7
252 天前
美团技术团队,2014 年的距离优化文档,希望能帮助你
https://tech.meituan.com/2014/09/05/lucene-distance.html
encro
252 天前
你们这些人,

都当 mysql 开发团队是 SB !!!

开发个简单的 geo 还比 pg 性能差一大截?
shenjinpeng
252 天前
推荐改用 es 查, 附带搜索一起搞了


PS: 贴一下 MySql 代码

```PHP
// 6371000 地球半径, 距离查询
// 可以当作子查询字段排序,先计算距离再排序
$distance = "ROUND( 6371000 * 2 * ASIN(SQRT( POW(SIN(({$lat} * PI() / 180 - latitude * PI() / 180) / 2),2) + COS({$lat} * PI() / 180) * COS(latitude * PI() / 180) * POW(SIN(({$lng} * PI() / 180 - longitude * PI() / 180) / 2),2))) ) AS distance";

```


```mysql
-- 自己实现的 mysql 函数
CREATE DEFINER=`root`@`%` FUNCTION `calcStoreDistance`(lat double, lng double,latitude double,longitude double) RETURNS int(11)
BEGIN
RETURN ROUND(
12742000 * ASIN(SQRT(
POW(SIN((lat * PI() / 180 - latitude * PI() / 180) / 2),2) +
COS(lat * PI() / 180) *
COS(latitude * PI() / 180) *
POW(SIN((lng * PI() / 180 - longitude * PI() / 180) / 2),2)
)));
END


-- 使用 SELECT *,calcStoreDistance(25.028317,102.678467,store.latitude,store.longitude) as distance FROM `store` order by distance;
```


```mysql
-- 直接用 mysql 自带函数
ROUND(st_distance_sphere(point($lng,$lat),geo)) as distance
```
Orlion
252 天前
https://blog.fanscore.cn/a/51/ 看下我这这篇文章吧,不知道对你有没有帮助,大概思路是取出一个坐标点附近九宫格所有坐标点,然后在程序中计算每个点距离该坐标点的距离,过滤掉不符合距离条件的点。

虽然文章中使用的是 redis ,但换成 mysql 应该是通用的吧。
opengps
252 天前
这问题刚好我以前做围栏时候用过,办法就是一楼所说。按照纬度 1 度相差 111km 来算,10 公里刚好就是 0.1 度,然后你再根据需要二次筛选即可(甚至不筛选也够用了)
QlanQ
252 天前
mysql 高版本的已经支持了,20w 数据 还是可以应付了,等公司有钱了,快上市了,流量起来了,有时间了你在改一版吧,改成 es ,把前端相关查询都走 es(es 真的很费钱,小公司单台服务器配置差点的,都跑不起来,比如 2G 内存的)
summerLast
252 天前
1. 当前经纬度各加减 50 米
2. 35.35 米外 50 米内坐标转换算斜边长度大于 50 米的
1+2
echoZero
252 天前
之前做过 根据用户位置查找多少米的商户,用 redis 的 GEO 算的
Desdemor
252 天前
俺们用的 redis
qsnow6
252 天前
GEO 是最佳实践,优化好索引速度很快,上集群纯粹没活硬整,集群间的通信不需要开销吗?
wanniwa
252 天前
@ffw5b7 #44 感谢分享,收藏了
tool2d
252 天前
我用 google 的 http://s2geometry.io/ ,查询效率很高。

这算法 mongodb 有内置空间索引,mysql 似乎没有。
griffen
252 天前
neo4j 比较适用于这类数据吧?
fengpan567
252 天前
我建议直接换 mongodb
xz410236056
252 天前
@xmumiffy #1 范围是个圆形。。。
eachann
252 天前
1. 使用 MySQL 的空间数据类型`POINT`存储位置经纬度是合适的。
2. 使用 MySQL 的空间索引和`ST_Distance_Sphere`函数可以快速查询半径 10 公里内的所有店铺。

解释:
1. MySQL 的空间数据类型`POINT`可以存储地理位置的经纬度信息。与传统的两个分开的字段相比,`POINT`类型可以让 MySQL 利用空间索引( Spatial Index ),这样可以大大提高地理位置查询的效率。
2. `ST_Distance_Sphere`函数可以计算两个点之间的球面距离,适用于地球表面的距离计算。结合空间索引,这种方法可以快速筛选出指定半径内的店铺。

建议:
- 确保你的 MySQL 版本支持空间数据类型和函数。MySQL 5.7 及以上版本对空间数据的支持较好。
- 在包含经纬度的`POINT`字段上创建空间索引,以提高查询效率。
- 使用如下 SQL 示例进行查询:

```sql
SELECT shop_name, ST_AsText(location)
FROM shops
WHERE ST_Distance_Sphere(location, ST_GeomFromText('POINT(经度 纬度)')) <= 10000;
```
- 替换`经度`和`纬度`为你的指定坐标。这个查询会返回距离指定坐标 10 公里内的所有店铺的名称和位置。
ElmerZhang
252 天前
我用 postgis 。不过我记得 mysql 5.7 开始就支持 geo 了吧?

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1022313

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX