Redis GEO 功能使用场景

前段时间自己在做附近直播相关业务,其中有一个核心的点就是检索用户附近的主播,也是主要召回池。针对业务场景的特殊性,最后决定使用Redis的GEO技术来完成这个功能。主要考虑点在于每天在线直播的主播数量是固定的差不多一万这个量级,使用配置好一点的单机Redis单key存储是没问题的。数据操作主要有两个:一是主播开播的时候写入主播Id的经纬度,二是主播关播的时候删除主播Id元素。这样就维护了一个具有位置信息的在线主播集合提供给线上检索。下面详细介绍一下。

Redis GEO 命令

Redis3.2 版本提供了GEO(地理信息定位)功能,支持存储地理位置信息用来实现诸如附近位置、摇一摇这类依赖于地理位置信息的功能,对于需要实现这些功能的开发者来说是一大福音。GEO功能是Redis的另一位作者Matt Stancliff 借鉴NoSQL数据库 Ardb 实现的,Ardb的作者来自中国,它提供了优秀的GEO功能。

Redis GEO 相关的命令如下:

# 添加一个空间元素,longitude、latitude、member分别是该地理位置的经度、纬度、成员 # 这里的成员就是指代具体的业务数据,比如说用户的ID等 # 需要注意的是Redis的纬度有效范围不是[-90,90]而是[-85,85] # 如果在添加一个空间元素时,这个元素中的menber已经存在key中,那么GEOADD命令会返回0,相当于更新了这个menber的位置信息 GEOADD key longitude latitude member [longitude latitude member] # 用于添加城市的坐标信息 geoadd cities:locations 117.12 39.08 tianjin 114.29 38.02 shijiazhuang 118.01 39.38 tangshan 115.29 38.51 baoding # 获取地理位置信息 geopos key member [member ...] # 获取天津的坐标 geopos cities:locations tianjin # 获取两个坐标之间的距离 # unit代表单位,有4个单位值 - m (meter) 代表米 - km (kilometer)代表千米 - mi (miles)代表英里 - ft (ft)代表尺 geodist key member1 member2 [unit] # 获取天津和保定之间的距离 GEODIST cities:locations tianjin baoding km # 获取指定位置范围内的地理信息位置集合,此命令可以用于实现附近的人的功能 # georadius和georadiusbymember两个命令的作用是一样的,都是以一个地理位置为中心算出指定半径内的其他地理信息位置,不同的是georadius命令的中心位置给出了具体的经纬度,georadiusbymember只需给出成员即可。其中radiusm|km|ft|mi是必需参数,指定了半径(带单位),这两个命令有很多可选参数,参数含义如下: # - withcoord:返回结果中包含经纬度。 # - withdist:返回结果中包含离中心节点位置的距离。 # - withhash:返回结果中包含geohash,有关geohash后面介绍。 # - COUNT count:指定返回结果的数量。 # - asc|desc:返回结果按照离中心节点的距离做升序或者降序。 # - store key:将返回结果的地理位置信息保存到指定键。 # - storedist key:将返回结果离中心节点的距离保存到指定键。 georadius key longitude latitude radiusm|km|ft|mi [withcoord] [withdist] [withhash] [COUNT count] [asc|desc] [store key] [storedist key] georadiusbymember key member radiusm|km|ft|mi [withcoord] [withdist] [withhash] [COUNT count] [asc|desc] [store key] [storedist key] # 获取geo hash # Redis使用geohash将二维经纬度转换为一维字符串,geohash有如下特点: # - GEO的数据类型为zset,Redis将所有地理位置信息的geohash存放在zset中。 # - 字符串越长,表示的位置更精确,表3-8给出了字符串长度对应的精度,例如geohash长度为9时,精度在2米左右。长度和精度的对应关系,请参考:https://easyreadfs.nosdn.127.net/9F42_CKRFsfc8SUALbHKog==http://www.likecs.com/8796093023252281390 # - 两个字符串越相似,它们之间的距离越近,Redis利用字符串前缀匹配算法实现相关的命令。 # - geohash编码和经纬度是可以相互转换的。 # - Redis正是使用有序集合并结合geohash的特性实现了GEO的若干命令。 geohash key member [member ...] # 删除操作,GEO没有提供删除成员的命令,但是因为GEO的底层实现是zset,所以可以借用zrem命令实现对地理位置信息的删除。 zrem key member Redis GEO 原理

讲Redis GEO实现之前需要先明白一些关于空间索引的算法GEOHASH的知识。针对索引我们日常所见都是一维的字符,那么如何对三维空间里面的坐标点建立索引呢,直接点就是三维变二维,二维变一维。

地球纬度区间是[-90,90],经度区间是[-180,180]。 将它展开想象成一个矩形。

img

通过上面的方法将地球的表面转换成二维空间的平面,那接下来就是如何将二维换行成一维了。我们先将平面切割成四个正方形,然后用简单的 01 编码来标识这个四个正方形,最后按照编码的大小将四个正方形连接起来,这样整个平面就转换成了一条Z曲线,变成了一维。我们递归对每个正方形做同样的操作,递归的层次越深,整个平面就逐渐被Z曲线填充。我们的点也会落在每个小正方形里面,小正方形越小,精度就越高。如下图所示:

img

转成一维以后接下来就如何建立索引了。当我们拿到一个经纬度之后按照如下方式进行编码。

img

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpyfjx.html