-
经纬度坐标使用关系数据库 (元素 id, 经度 x, 纬度 y) 存储,可以指定一个半径 r 使用一条 SQL 圈出一定范围内的元素。
1 2 3 4 5 6 7
SELECT id FROM positions WHERE x0 - r < x < x0 + r AND y0 - r < y < y0 + r
-
GeoHash 算法将二维的经纬度数据映射到一维的整数,这样所有的元素都将在挂载到一条线上,距离靠近的二维坐标映射到一维后的点之间距离也会很接近。
-
映射算法实际是对地图进行精细切分,然后对这些方格进行整数编码。编码后每个地图元素的坐标都将变成一个整数,越是靠近的方格编码越是接近。
- 切分时使用二进制整数表示方格,如(00,01,10,11),精度越高,二进制整数越长。编码之后,每个地图元素的坐标都将变成一个整数。GeoHash 算法会继续对这个整数做一次
base32
编码成一个字符串,使用 zset 的value-score
结构进行存储。value
储存元素的 key,score
储存元素的 52 位整数值。 - Redis 的 Geo 数据结构,它们将全部放在一个 zset 集合中,在集群环境中单个 key 对应的数据量不宜超过
1M
,否则会导致集群迁移出现 卡顿现象,影响线上服务的正常运行。 - 建议 Geo 的数据使用单独的 Redis 实例部署,不使用集群环境。如果数据量过大,需要对 Geo 数据按适合维度进行拆分。
- Redis 的 GeoHash 只有 6 个基本指令,没有提供 geo 删除指令:
# 增加元素
127.0.0.1:6379> geoadd company 116.48105 39.996794 juejin
(integer) 1
127.0.0.1:6379> geoadd company 116.562108 39.787602 jd 116.334255 40.027400 xiaomi
(integer) 2
# 获取两元素间距离,距离单位可以是 m、km、ml、ft
127.0.0.1:6379> geodist company juejin ireader km
"10.5501"
# 获取元素位置
127.0.0.1:6379> geopos company ireader
1) 1) "116.5142020583152771"
2) "39.90540918662494363"
127.0.0.1:6379> geopos company juejin ireader
1) 1) "116.48104995489120483"
2) "39.99679348858259686"
2) 1) "116.5142020583152771"
2) "39.90540918662494363"
# 获取元素的 hash 值
# 可以使用这个编码值去 http://geohash.org/${hash}中进行直接定位
127.0.0.1:6379> geohash company ireader
1) "wx4g52e1ce0"
# 根据元素查询附近元素(不会排除自身)
# 范围 20 公里以内最多 3 个元素按距离正排
127.0.0.1:6379> georadiusbymember company ireader 20 km count 3 asc
1) "ireader"
2) "juejin"
3) "meituan"
# 范围 20 公里以内最多 2 个元素按距离正排,并显示距离、哈希值、坐标
127.0.0.1:6379> georadiusbymember company ireader 20 km withcoord withdist withhash count 2 asc
1) 1) "ireader"
2) "0.0000"
3) (integer) 4069886008361398
4) 1) "116.5142020583152771"
2) "39.90540918662494363"
2) 1) "juejin"
2) "10.5501"
3) (integer) 4069887154388167
4) 1) "116.48104995489120483"
2) "39.99679348858259686"
# 根据坐标值查询附近的元素
127.0.0.1:6379> georadius company 116.514202 39.905409 20 km withdist count 2 asc
1) 1) "ireader"
2) "0.0000"
2) 1) "juejin"
2) "10.5501"