「进击Redis」十七、保证你没用过Redis GEO

前言

是的,我不是标题党,我保证好哥哥们都没有用过,用过的就不是好哥哥(诶诶诶,好哥哥们轻点,别打脸)。实话说,要是我不弄 Redis 的这个专题博客,我还真不知地道有像HyperLogLogBitmapsLua等相关的功能。我相信好多好哥哥们也跟我一样,对整个 Redis 都是只会用其中的某些基础东西,你看看这不是有学到了吗。今天这个的话一样的我还是没有用过,但是我好学呀,弄它,明年面试(不是猛男我啊,我学这个可不是为了装*)的时候又可以装一波,装完就跑的那种。
装逼

概述

Redis3.2 版本提供了GEO(地理信息定位)功能,GEO 主要用于存储地理位置信息,并对存储的信息进行操作,用于实现诸如附近位置、摇一摇这类依赖于地理位置信息的功能,对于需要实现这些功能的开发者来说是一大福音。

命令

增加地理位置信息

Redis 提供geoadd命令来添加或者更新地理信息位置,其中longitudelatitudemember分别是该地理位置的经度、纬度、成员。添加如下图的城市经纬度
城市经纬度

## 格式
geoadd key longitude latitude member [longitude latitude member ...]
## 添加北京,返回结果代表添加成功的个数,假如已经存在了则返回0
127.0.0.1:6379> geoadd cities:locations 116.28 39.55 beijing
(integer) 1

获取地理信息位置

## 格式 ,member 成员
geopos key member [member ...]
## 获取天津的地理位置
127.0.0.1:6379> geopos cities:locations tianjin
1) 1) "117.12000042200088501"
2) "39.0800000535766543"

获取两个地理位置的距离

Redis提供了geodist来获取两个地理位置的距离。其中unit代表返回结果的单位,包含以下四种:

  1. m(meters)代表米。
  2. km(kilometers)代表公里。
  3. mi(miles)代表英里。
  4. ft(feet)代表尺。
## 格式
geodist key member1 member2 [unit]
## 计算北京到天津的距离
127.0.0.1:6379> geodist cities:locations tianjin beijing km
"89.2061"

获取指定位置范围内的地理信息位置集合

georadiusgeoradiusbymember两个命令的作用是一样的,都是以一个地
理位置为中心算出指定半径内的其他地理信息位置,不同的是georadius命令
的中心位置给出了具体的经纬度,georadiusbymember只需给出成员即可。其
radiusm|km|ft|mi是必需参数,指定了半径(带单位),这两个命令有很多
可选参数,如下:

  1. withcoord:返回结果中包含经纬度。
  2. withdist:返回结果中包含离中心节点位置的距离。
  3. withhash:返回结果中包含 geohash,有关 geohash 后面介绍。
  4. COUNT count:指定返回结果的数量。
  5. asc|desc:返回结果按照离中心节点的距离做升序或者降序。
  6. store key:将返回结果的地理位置信息保存到指定键。
  7. 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]
## 计算五座城市中,距离北京150公里以内的城市
127.0.0.1:6379> georadiusbymember cities:locations beijing 150 km
1) "beijing"
2) "tianjin"
3) "tangshan"
4) "baoding"

获取 GeoHash

GeoHash

GeoHash本质上是空间索引的一种方式,其基本原理是将地球理解为一个二维平面,将平面递归分解成更小的子块,每个子块在一定经纬度范围内拥有相同的编码。以 GeoHash 方式建立空间索引,可以提高对空间 poi 数据进行经纬度检索的效率。具体可以看Geohash 原理
使用 geohash 将二维经纬度转换为一维字符串,geohash有如下特点:

  1. GEO 的数据类型为zset,Redis 将所有地理位置信息的geohash存放在zset中。
  2. 字符串越长,表示的位置更精确,例如geohash长度为 9 时,精度在 2 米左右。如下图
  3. 两个字符串越相似,它们之间的距离越近,Redis 利用字符串前缀匹配算法实现相关的命令。
  4. geohash编码和经纬度是可以相互转换的。
    长度
## 格式
geohash key member [member ...]
## 计算北京的geohash
127.0.0.1:6379> geohash cities:locations beijing
1) "wx4ww02w070"

删除地理位置信息

GEO没有提供删除成员的命令,但是因为GEO的底层实现是zset,所以
可以借用zrem命令实现对地理位置信息的删除。

## 格式
zrem key member
127.0.0.1:6379> zrem cities:locations beijing
1

原理

增加地理位置原理

上面有说GEO的底层数据结构是zset(sorted set),这个基本的数据结构不了解的话可以看Redis Sorted Set 运用场景、API 解析。先看一个zset的新增命令zadd key score member [score member ...],好哥哥们发现了吗,是不是很相似。有比较杠的好哥哥就会说了,这哪里相似,GEO明显有经纬度两个值,而zset只有一个score。是的,实际上这个地方是运用了一个算法那就是通过经纬度计算出对应的 52 位(bit)的 GEOHASH(这个其实也是一个算法,上面有提到过) 值作为元素的 Score值。那这样是不是就和zset一样了呢。所以其存储的原理和zset是一样的,只是多了一个计算Score值的过程。

geohash 原理分析

这个实际上上面已经有提到了,如果好哥哥们没有看懂那篇的话猛男我又找了一篇比较好懂的,可以看下GeoHash 核心原理解析,如果不想看的话那我总结如下(好哥哥们我都这样了你确定还不点赞加个关注吗)。
理解GeoHash需要解决以下两个问题:

  1. 如何唯一表示地球上的一块空间?
  2. 如何将地球切分成大小近似的区块,并支持不同粒度的表示?

方案:

  1. 将三维地球变成二维:
    地球纬度区间是[-90,90],经度区间是[-180,180]。 将它展开想象成一个很大的矩形。
  2. 将二维再转成一维:
    通过第一步的方法,我们能够将地球的表面转换成二维空间的平面。那接下来要将二维转变成一维。如果切割二维空间,可以切割出很多正方形。如何表示这个正方形呢?最简单的方法是在平面上进行遍历。每遍历到一个点,就给它标注一个值,比如 00、01、10、11,随着二进制数字增加,相当于遍历面上不同的位置。当将空间划分为四块时候,编码的顺序分别是左下角 00,左上角 01,右下脚 10,右上角 11,也就是类似于 Z 的曲线。
  3. 将一维表示成二进制码存储:
    Geohash 也有几种编码形式,常见的有 2 种,base 32base 36。 会将落到网格中的二进制数据编码成字符串。

使用场景

这个上面已经提到了,例如附近位置、附件的人、摇一摇这类依赖于地理位置信息的功能都可以使用GEO来实现。

总结

这一篇关于地理信息定位GEO,没有讲的太深,这个篇幅就已经很长了。主要的一个难点就是在于GeoHash的一个计算算法上,要搞懂这个的话好哥哥们还是要看看上面的两篇文章,当然也可以自己去搜索相关的资料,讲道理这个功能还是挺好玩的。后面有机会去踩踩坑,有熟悉的好哥哥可以把坑留着评论区。

本期就到这啦,有不对的地方欢迎好哥哥们评论区留言,另外求关注、求点赞

上一篇: Redis 发布/订阅原理解析

猜你喜欢

转载自blog.csdn.net/qq_34090008/article/details/111410740
GEO