共计 2337 个字符,预计需要花费 6 分钟才能阅读完成。
简述
移动互联网时代 LBS 应用越来越多,交友软件中附近的小姐姐、外卖软件中附近的美食店铺、打车软件附近的车辆等等,那这种附近各种形形色色的 XX 是如何实现的呢
我么你都知道地球上的地理位置是使用二维的经纬度表示,经度范围 (-180, 180),纬度范围 (-90, 90),只要我们确定一个点的经纬度就可以明确在地球上的位置。
redis3.2 版本新增的一个功能就是对 GEO(地理位置)的支持。
地理位置大概提供了 6 个命令,分别为:
- GEOADD
- GEODIST
- GEOHASH
- GEOPOS
- GEORADIUS
- GEORADIUSBYMEMBER
使用
添加地理位置
将给定的空间元素(纬度、经度、名字)添加到指定的键里面
GEOADD key longitude latitude member [longitude latitude member …]
# 如添加杭州北京上海的地理位置 | |
127.0.0.1:6379> geoadd city 120.20000 30.26667 hangzhou 116.41667 39.91667 beijing 121.47 31.23 shanghai |
获取地理位置信息
从键里面返回所有给定位置元素的位置(经度和纬度),可以获取集合中任意元素的经纬度坐标,可以一次获取多个
GEOPOS key member [member …]
127.0.0.1:6379> geopos city hangzhou beijing shanghai | |
1) 1) "120.15000075101852417" | |
2) "30.2800007575645509" | |
2) 1) "116.39999896287918091" | |
2) "39.90000009167092543" | |
3) 1) "121.47000163793563843" | |
2) "31.22999903975783553" | |
127.0.0.1:6379> geopos city hangzhou | |
1) 1) "120.15000075101852417" | |
2) "30.2800007575645509" |
计算距离
距离单位可以是 m、km、ml、ft,分别代表米、千米、英里和尺。如果用户没有显式地指定单位参数,那么 GEODIST
默认使用米作为单位。
GEODIST key member1 member2 [unit]
127.0.0.1:6379> geodist city shanghai hangzhou km | |
"164.5694" | |
127.0.0.1:6379> geodist city beijing hangzhou km | |
"1122.7998" |
[info]
GEODIST
命令在计算距离时会假设地球为完美的球形,在极限情况下,这一假设最大会造成 0.5% 的误差。
获取指定元素范围的地理信息位置集合
使用 GEORADIUSBYMEMBER
命令即可查询附近的位置
GEORADIUSBYMEMBER key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [ASC|DESC] [COUNT count]
# 例如查找距离杭州 300km 以内的城市的 10 个城市按距离排序 | |
127.0.0.1:6379> GEORADIUSBYMEMBER city hangzhou 300 km WITHCOORD WITHDIST WITHHASH ASC COUNT 10 | |
1) 1) "hangzhou" | |
2) "0.0000" | |
3) (integer) 4054134257390783 | |
4) 1) "120.15000075101852417" | |
2) "30.2800007575645509" | |
2) 1) "shanghai" | |
2) "164.5694" | |
3) (integer) 4054803462927619 | |
4) 1) "121.47000163793563843" | |
2) "31.22999903975783553" |
-
WITHDIST
:在返回位置元素的同时,将位置元素与中心之间的距离也一并返回。距离的单位和用户给定的范围单位保持一致。 -
WITHCOORD
:将位置元素的经度和维度也一并返回。 -
WITHHASH
:以 52 位有符号整数的形式,返回位置元素经过原始 geohash 编码的有序集合分值。这个选项主要用于底层应用或者调试,实际中的作用并不大。
获取元素的 hash 值
返回的其实是元素的经纬度经过 goehash 计算后的 base32 编码字符串。可以通过连接 http://geohash.org/${hash}中进行直接定位,它是 geohash 的标准编码值。
127.0.0.1:6379> geohash city hangzhou | |
1) "wtmkq069cc0" | |
127.0.0.1:6379> geohash city beijing | |
1) "wx4fbxxfke0" |
原理
存储结构
其存储结构主要使用的是 Redis 的有序结构,其 score 是 GeoHash 的 52 位整数值
127.0.0.1:6379> ZRANGE city 0 -1 WITHSCORES | |
1) "hangzhou" | |
2) "4054134257390783" | |
3) "shanghai" | |
4) "4054803462927619" | |
5) "beijing" | |
6) "4069885360207904" |
原理
其原理比较容易理解,核心思想就是将球体转换为球面,区块转换为一点
主要分为三步
- 将三维的地球变为二维的坐标
- 在将二维的坐标转换为一维的点块, 并进行标记
- 最后将一维的点块转换为二进制在通过 base32 编码(比如 wtmkq069cc0)
- 在标记的点块中找到 w 块,在 w 块中找到 t 块,在 t 块中找到 m 块 …..
详细原理解析可以参考这边文章 GeoHash 核心原理解析
其他 Geo 处理
目前很多数据存储引擎都支持 Geo 的处理,如 MongoDB、MySql、PgSql、Elasticsearch 等等。