本文来源:https://www.dazhuanlan.com/2020/02/05/5e3a0a3110649/

背景

前段时间自己在做附近直播相关业务,其中有一个核心的点就是检索用户附近的主播,也是主要召回池。针对业务场景的特殊性,最后决定使用RedisGEO技术来完成这个功能。主要考虑点在于每天在线直播的主播数量是固定的差不多一万这个量级,使用配置好一点的单机Rediskey存储是没问题的。数据操作主要有两个:一是主播开播的时候写入主播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==/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]。 将它展开想象成一个矩形。

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

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

从上面的图可以发现二分的次数越多就越接近经纬度的实际值,和前面提到的不断递归正方形是一个意思。按照上面的方式我们选定一个二分的深度(也就是精度)分别对经纬度进行编码。然后按照以奇数为纬度,偶数为经度组合成一个二进制序列,再将获取到的经纬度组合二进制序列以每5个数为一组,将每一组都进行转换成十进制数字,最后采用Base32对应编码规则进行转换可得到编码,也就是最后的索引。

通过上面几个步骤介绍了一下GeoHash具体的流程、有了上面这个知识点,理解Redis GEO原理就很简单了,Redis使用ZSet的方式存储Geo类型的数据,有序集合里面的member是具体的业务对象,score就是该业务对象的经纬度进行GeoHash编码之后将二级制序列转成52位整数值数据。当我们想要获取某个经纬度附近的元素时候,先根据当前经纬度计算出对应的GeoHash块(52位整数值),在根据半径计算出当前hash块周围的8个hash块,然后在根据score值获取这8个hash块范围内的元素返回。

GEO HASH 延伸

对于一个经纬度,如果我们编码的时候选择对经度二分3次(3位二进制),对维度二分2次(2位二进制),最后组合成一个5位的二级进序列,经过Base32编码得到一个字符。那么这个字符的一共有2^5=32个,这样就将地图划分为32个块。如下图所示

GeoHash将每一个区域画成一块块矩形块,每个矩形块使用一个字符串表示,当我们需要查询附近的点时,通过自己的坐标计算出一个字符串,根据这个字符串定位到我们所在的矩形块,然后返回这个矩形块中的点。然后根据编码的深度来确定精度,或者根据Base32编码之后字符的长度来确定块的所表示的区域大小。

length width height
1 5000km 5000km
2 1250km 625km
3 156km 156km
4 39.1km 19.5km
5 4.89km 4.89km
6 1.22km 0.61km
7 153m 153m
8 38.2m 19.1m
9 4.77m 4.77m
10 1.19m 0.596m
11 149mm 149mm
12 37.2mm 18.6mm

对于这样的编码方式有一定的局限性:在拥有局部保序性的同时,具有突变性。导致一些邻近点真实并不是距离较近的点。

参考

http://geohash.gofreerange.com/

https://halfrost.com/go_spatial_search/

https://www.cnblogs.com/LBSer/p/3310455.html

Redis GEO 功能使用场景的更多相关文章

  1. redis各种数据结构使用场景

    一.redis 数据结构使用场景 原来看过 redisbook 这本书,对 redis 的基本功能都已经熟悉了,从上周开始看 redis 的源码.目前目标是吃透 redis 的数据结构.我们都知道,在 ...

  2. Spring Boot 2 实战:利用Redis的Geo功能实现查找附近的位置

    1. 前言 老板突然要上线一个需求,获取当前位置方圆一公里的业务代理点.明天上线!当接到这个需求的时候我差点吐血,这时间也太紧张了.赶紧去查相关的技术选型.经过一番折腾,终于在晚上十点完成了这个需求. ...

  3. Redis Geo: Redis新增位置查询功能

    转载于:http://www.itxuexiwang.com/a/shujukujishu/redis/2016/0216/144.html 移动互联网增进了人与人之间的联系,其中基于位置信息的服务( ...

  4. 转:Redis Geo: Redis新增位置查询功能

    原文来自于:http://www.infoq.com/cn/news/2015/07/redis-geo 移动互联网增进了人与人之间的联系,其中基于位置信息的服务(Location Based Ser ...

  5. 3.Redis高级功能

    3.Redis高级功能3.1 慢查询分析3.1.1 慢查询的两个配置参数3.1.2 最佳实践3.1.3 单线程架构3.2 Redis Shell3.2.1 redis-cli 详解3.2.2 redi ...

  6. redis数据类型及使用场景

    Redis数据类型  String: Strings 数据结构是简单的key-value类型,value其实不仅是String,也可以是数字. 常用命令:  set,get,decr,incr,mge ...

  7. (3)redis队列功能

    Redis队列功能介绍 List 常用命令: Blpop删除,并获得该列表中的第一元素,或阻塞,直到有一个可用 Brpop删除,并获得该列表中的最后一个元素,或阻塞,直到有一个可用 Brpoplpus ...

  8. Redis数据结构以及应用场景

    1. Redis数据结构以及应用场景 1.1. Memcache VS Redis 1.1.1. 选Memcache理由 系统业务以KV的缓存为主,数据量.并发业务量大,memcache较为合适 me ...

  9. redis GEO地理位置命令介绍

    GEOADD keylongitude latitude member [longitude latitude member ...] Available since 3.2.0. Time comp ...

随机推荐

  1. 阿里面试官:HashMap 熟悉吧?好的,那就来聊聊 Redis 字典吧!

    最近,小黑哥的一个朋友出去面试,回来跟小黑哥抱怨,面试官不按套路出牌,直接打乱了他的节奏. 事情是这样的,前面面试问了几个 Java 的相关问题,我朋友回答还不错,接下来面试官就问了一句:看来 Jav ...

  2. Dos简易基础及常用Dos命令

    Dos简易基础及常用Dos命令 什么是cmd? cmd是command的缩写,意指操作系统中的命令行程序,一般说的都是Windows中的Dos系统. 如何打开cmd? 键盘操作:Win + R 输入c ...

  3. jQuery提供的Ajax方法

    jQuery提供了4个ajax方法:$.get()  $.post()  $.ajax()  $.getJSON() 1.$.get() $.get(var1,var2,var3,var4): 参数1 ...

  4. pytest132个命令行参数用法

    在Shell执行pytest -h可以看到pytest的命令行参数有这10大类,共132个 序号 类别 中文名 包含命令行参数数量 1 positional arguments 形参 1 2 gene ...

  5. python基础四(json\os\sys\random\string模块、文件、函数)

    一.文件的修改 文件修改的两种思路: 1.把文件内容拿出来,做修改后,清空原来文件的内容,然后把修改过的文件内容重新写进去. 步骤: 1.打开文件:f=open('file','a+')  #必须用a ...

  6. vmware启动winodws时报错弹出【无法连接MKS:套接字连接尝试次数太多;正在放弃;】

    启动虚拟机时报错 解决办法:(其实就是有关于虚拟机的服务没有起) win+R输入services.msc,将所有有关vmware的服务都起起来即可

  7. 我的Python自学之路-002 字典的知识

    '''字典是python中唯一的验证类型,采用键值对(key-value)的形式存储数据.python对key进行哈希函数运算.根据计算的结果决定value的存储地址.所以字典是无序存储的.且key必 ...

  8. 云计算openstack核心组件——nova计算服务(7)

    一.nova介绍:       Nova 是 OpenStack 最核心的服务,负责维护和管理云环境的计算资源.OpenStack 作为 IaaS 的云操作系统,虚拟机生命周期管理也就是通过 Nova ...

  9. Mac使用Charles给iPhone抓包流程

    目录 需求 步骤 1. Mac打开共享Wifi 2. iPhone连接刚刚的WIFI 3. 找到Mac的局域网ip 4. 配置代理 需求 有时候手机接口出问题了,不知道从哪里下手,Charles就是非 ...

  10. 如何解决Python下 pip install module 下载慢解决方法?

    对于Python来编程的用户最大的一个痛点就是,下载模块是下载速度特别慢,那么有没有解决方法呢? 换Python的pip下载源 1.首先安装一个模块 pqi,在cmd下 pip install pqi ...