利用 GEO 实现查看附近功能的几个方案

利用 GEO 实现查看附近功能的几个方案

author: he xiaodong date: 2019-07-19

查看附近:此功能需要注意的是,经纬度坐标的密度不一样 (地球是一个椭圆),勾股定律计算平方差时之后再求和时,需要按一定的系数比加权求和,如果不求精确的话,也可以不必加权。

MySQL 直接存储经纬度

适合场景:数据较少,例如查看全国的xx奢侈品店,这些数据加起来也不一定会有十万条,所以可以忽略考虑性能。直接使用数据库存储经度,纬度,然后sql查询就可以的,为了满足高性能的矩形区域算法,数据表需要在经纬度坐标加上双向复合索引(x, y),这样可以最大优化查询性能。 select id from positions where x0-r < x < x0+r andy0-r < y < y0+r 高并发情况下也会面临瓶颈

Redis GeoHash来实现:

业界比较通用的地理位置距离排序算法是 GeoHash 算法,Redis 也使用 GeoHash 算法。GeoHash 算法将二维的经纬度数据映射到一维的整数,这样所有的元素都将在挂载到一条线上,距离靠近的二维坐标映射到一维后的点之间距离也会很接近。当我们想要计算「附近的人时」,首先将目标位置映射到这条线上,然后在这个一维的线上获取附近的点就行了。那这个映射算法具体是怎样的呢?它将整个地球看成一个二维平面,然后划分成了一系列正方形的方格,就好比围棋棋盘。所有的地图元素坐标都将放置于唯一的方格中。方格越小,坐标越精确。然后对这些方格进行整数编码,越是靠近的方格编码越是接近。那如何编码呢?一个最简单的方案就是切蛋糕法。设想一个正方形的蛋糕摆在你面前,二刀下去均分分成四块小正方形,这四个小正方形可以分别标记为 00,01,10,11 四个二进制整数。然后对每一个小正方形继续用二刀法切割一下,这时每个小小正方形就可以使用 4bit 的二进制整数予以表示。然后继续切下去,正方形就会越来越小,二进制整数也会越来越长,精确度就会越来越高。

真实算法中还会有很多其它刀法,最终编码出来的整数数字也都不一样。 编码之后,每个地图元素的坐标都将变成一个整数,通过这个整数可以还原出元素的坐标,整数越长,还原出来的坐标值的损失程度就越小。对于「附近的人」这个功能而言,损失的一点精确度可以忽略不计。 GeoHash 算法会继续对这个整数做一次 base32 编码 (0-9,a-z 去掉 a,i,l,o 四个字母) 变成一个字符串。

在 Redis 里面,经纬度使用 52 位的整数进行编码,放进了 zset 里面,zset 的 value 是元素的key,score 是 GeoHash 的 52 位整数值。zset 的score 虽然是浮点数,但是对于 52 位的整数值,它可以无损存储。 在使用 Redis 进行 Geo 查询时,我们要时刻想到它的内部结构实际上只是一个 zset(skiplist)。通过 zset 的 score 排序就可以得到坐标附近的其它元素 (实际情况要复杂一些,不过这样理解足够了),通过将 score 还原成坐标值就可以得到元素的原始坐标。

然后使用georadiusbymember查询指定元素附近的其它元素,它的参数非常复杂。参考链接 因为这个底层还是zset 结构,数据量很大的情况下,会占用很大的空间。

(部分内容来自掘金付费小册,作者:老钱) 参考链接:http://blog.jobbole.com/80633/

使用elsaticsearch 的地理位置搜索来实现

首先是创建相对应的地址位置,此时将决定最终的距离偏差的精度,先存储位置信息,然后使用elasticsearch自带的一些查询来计算信息,例如:geo query, geoboundling box query, geoshape query等

相关参考: