Redis学习(12)-GeoHash

771 阅读4分钟

本文已参与[新人创作礼]活动,一起开启掘金创作之路。

近水楼台--GeoHash

reids 在3.2版本以后增加了地理位置Geo模块

GeoHash算法

业界比较通用的地理位置距离排序算法是GeoHash算法,Redis也使用GeoHash算法。GeoHash算法将二维的经纬度数据映射到一维的整数,这样所有的元素都将挂载到一条线上,距离靠近的二维坐标映射到一维后的点之间的距离也会很接近。

可以设想一下切蛋糕法。设想一个正方形的蛋糕摆在你面前,两刀下去均分分成四块小正方形,这四个小正方形可以分别标记为00、01、10、11共四个二进制整数。然后对每一个小正方形继续用两刀法切割,这时每个小小正方形就可以使用4bit的二进制整数表示。然后继续切下去,正方形就会越来越小,二进制整数也会越来越长,精确度就会越来越高。

GeoHash 算法会继续对这个整数做一次base32编码(09,az,去掉a、i、l、o四个字母)变成一个字符串。在Redis里面,经纬度使用52位的整数进行编码,放进了zset里面,zset的value是元素的key,score是GeoHash的52位整数值。zset的score虽然是浮点数,但是对于52位的整数值,它可以无损存储。

Geo指令的基本用法

增加

> geoadd company 116.48105 39.996794 juejin 116.51423 39.905409 ireader 
(integer) 2

Geo存储结构上使用的是zset,意味着我们可以使用zset相关的指令来操作Geo数据,所以元素删除指令可以直接使用zrem指令即可

距离

geodist 指令可以用来计算两个元素之间的距离,携带集合名称、两个名称和距离单位

> geodist company juejin ireader km
"10.5501"

获取元素位置

geopos 指令可以获取集合中任意元素的经纬度坐标,可以一次获取多个

> geopos company juejin
1) 1) "116.48104995489120483"
   2) "39.99679348858259686"

我们观察到获取的经纬度坐标和geoadd进去的坐标有少许误差,原因是GeoHash对二维坐标进行的一维映射是有损的,通过映射再还原回来的值会出现比较小的差别。对于"附近的人"这种功能来说,这点儿误差完全可以接受

获取元素的hash值

可以使用base32编码值去 geohash.org/${hash} 上进行直接定位,它是GeoHash的标准编码值

> geohash company ireader
1) "wx4g52e1ce0"

附近的公司

georadiusbymember 指令是最为关键的指令之一,它可以用来查询指定元素附近的其他元素。

# 范围 20 公里以内最多三个元素按距离正排,它不会排除自身
> georadiusbymember company ireader 20 km count 3 asc 
1) "ireader"
2) "juejin"
# 范围 20 公里以内最多三个元素按距离倒排
> georadiusbymember company ireader 20 km count 3 dsec
1) "juejin"
2) "ireader"
# 三个可选参数 withcoord、widthdist、withhash 用来携带附加参数
# withdist 很有用,它可以用来显示距离
> georadiusbymember company ireader 20 km withcoord withdist withhash count 3 asc 
1) 1) "ireader"
   2) "0.0000"
   3) (integer) 4069886008361398
   4) 1)"116.5142020583152771"
   4) 2)"39.90540918662494363"
# 根据经纬度查找附近元素 georadius (除参数为经纬度外,其余用法与 georadiusbymember 一致)
> georadius company 116.514202 39.905409 20 km widthdist count 3 asc
1) 1) "ireader"
   2) "0.0000"

注意事项

在一个地图应用中,车的数据、餐馆的数据、人的数据可能会有几百万条甚至几千万条,如果使用Redis的Geo数据结构,它们将都被放在一个zset集合中。在Redis的集群环境中,集合可能会从一个节点迁移到另一个节点,如果单个key的数据过大,会对集群的迁移工作造成较大的影响,在集群环境中单个key对应的数据量不宜超过1MB,否则会导致集群迁移出现卡顿现象,影响线上服务的正常运行,

建议Geo的数据使用单独的Redis实例部署,不使用集群环境。同时尽量按照省份对数据进行拆分