一、Geospatial概述
Redis 在 3.2 版本以后增加了地理位置 GEO 模块。
生活中我们常常见到的功能如:附近的人、查找共享单车、美团找附近的餐馆等用来指定两地之间的距离基本上都可以用这个来实现!
并且Geospatial本质是使用Sorted Set存储的,当然计算两者之间的距离Redis用的底层技术则是GeoHash(不了解的,可以看我下面贴出来的资料一)。
Redis对GEO模块添加地理位置的形式是:
key 经度 纬度 名称
- key:这个好理解我就不解释了
- 经度:从本初子午线0度开始,向左和向右分别分出180度,跨度是(-180,180),其中本初子午线向左称为西经,本初子午线向右称为东经。
- 纬度:以赤道0度开始,向上和向下分别分出90度,南极和北极分别为南纬90度和北纬90度,南极到北极的跨度是(-90,90),其中赤道到南极称为南纬,赤道到北极称为北纬。
- 名称:经度与纬度对应的地理名称
注意的点:
- 两级无法直接添加。
- 经纬度一定不能超出范围,有效的纬度从-85.05112878度到85.05112878度、有效的经度从-180度到180度。
- 地球是圆的,这两个数字的单位不是距离,而是角度。
学下面六个GEO命令之前,我们先来准备一下数据(城市经纬度)
获取城市经纬度:https://jingweidu.bmcx.com/
查看两地距离:http://www.24timemap.com/distance
- 北京:116.23128,40.22077
- 上海:121.48941,31.40527
- 重庆:106.54041,29.40268
- 广州:113.27324,23.15792
- 天津:117.30983,39.71755
- 深圳:113.88308,22.55329
有了以上的这些数据,我们就可以开始学习下面的六个命令了。
二、六大命令解析
2.1 GEOADD(geoadd)
**时间复杂度:**每一个元素添加是O(log(N)) ,N是sorted set的元素数量。
添加地理位置
案例:
127.0.0.1:6379> geoadd myMap 116.23128 40.22077 beijing #添加一个城市的地理坐标 (integer) 1 127.0.0.1:6379> geoadd myMap 121.48941 31.40527 shanghai 106.54041 29.40268 chongqing 113.27324 23.15792 guangzhou #添加多个 (integer) 3 127.0.0.1:6379> geoadd myMap 117.30983 39.71755 tianjing 113.88308 22.55329 shenzhen #添加多个 (integer) 2 127.0.0.1:6379>
2.2 GEOPOS(geopos)
时间复杂度:O(log(N))对于请求的每个成员,其中N是排序集中的元素数量。
获得对应位置的经纬度
返回值:
GEOPOS 命令返回一个数组, 数组中的每个项都由两个元素组成: 第一个元素为给定位置元素的经度, 而第二个元素则为给定位置元素的纬度。
当给定的位置元素不存在时, 对应的数组项为空值。
案例
127.0.0.1:6379> geopos myMap beijing #获取指定位置的下标 1) 1) "116.23128265142440796" 2) "40.22076905438526495" 127.0.0.1:6379> geopos myMap shanghai 1) 1) "121.48941010236740112" 2) "31.40526993848380499" 127.0.0.1:6379> geopos myMap shanghai tianjing #一次性获取多个地理坐标 1) 1) "121.48941010236740112" 2) "31.40526993848380499" 2) 1) "117.30983108282089233" 2) "39.71755086262169954" 127.0.0.1:6379>
2.3 GEODIST(geodist)
时间复杂度:O(log(N))
返回两地之间的距离,如果两个位置之间的其中一个不存在, 那么命令返回空值。
指定单位的参数 unit 必须是以下单位的其中一个:
- m 表示单位为米。
- km 表示单位为千米。
- mi 表示单位为英里。
- ft 表示单位为英尺。
如果用户没有显式地指定单位参数, 那么 GEODIST 默认使用米作为单位。
GEODIST 命令在计算距离时会假设地球为完美的球形, 在极限情况下, 这一假设最大会造成 0.5% 的误差。
案例:
127.0.0.1:6379> geodist myMap beijing tianjing km #北京与天津之间的直线距离,km "107.6344" 127.0.0.1:6379>
用上面我贴的工具验证一下:
我们注意到,这计算的还是很精准的。
2.4 GEORADIUS(georadius)
时间复杂度:O(N+log(M))其中,N为以圆心和半径划定的圆形区域的边框内的元素数量,M为索引内的项目数量。
以给定的经纬度为中心, 返回键包含的位置元素当中, 与中心的距离不超过给定最大距离的所有位置元素。
范围可以使用以下其中一个单位:
- m 表示单位为米。
- km 表示单位为千米。
- mi 表示单位为英里。
- ft 表示单位为英尺。
在给定以下可选项时, 命令会返回额外的信息:
WITHDIST: 在返回位置元素的同时, 将位置元素与中心之间的距离也一并返回。 距离的单位和用户给定的范围单位保持一致。
WITHCOORD: 将位置元素的经度和维度也一并返回。
WITHHASH: 以 52 位有符号整数的形式, 返回位置元素经过原始 geohash 编码的有序集合分值。 这个选项主要用于底层应用或者调试, 实际中的作用并不大。
命令默认返回未排序的位置元素。 通过以下两个参数, 用户可以指定被返回位置元素的排序方式:
- ASC: 根据中心的位置, 按照从近到远的方式返回位置元素。
- DESC: 根据中心的位置, 按照从远到近的方式返回位置元素。
案例:
127.0.0.1:6379> georadius myMap 112.89262 22.90026 100 km #返回指定经纬度为中心的100km范围内的城市 1) "guangzhou" 127.0.0.1:6379> georadius myMap 112.89262 22.90026 200 km withdist #返回指定经纬度为中心的200km范围内的城市和城市离中心的距离 1) 1) "shenzhen" 2) "108.6930" 2) 1) "guangzhou" 127.0.0.1:6379> georadius myMap 112.89262 22.90026 200 km withcoord #获取指定中心范围内的城市和经纬度 1) 1) "shenzhen" 2) 1) "113.88307839632034302" 2) "22.55329111565713873" 2) 1) "guangzhou" 2) 1) "113.27324062585830688" 2) "23.1579209662846921" 127.0.0.1:6379> georadius myMap 112.89262 22.90026 200 km withdist desc #带排序,远到近 1) 1) "shenzhen" 2) "108.6930" 2) 1) "guangzhou" 2) "48.3662" 127.0.0.1:6379> georadius myMap 112.89262 22.90026 200 km withdist asc #带排序,近到远 1) 1) "guangzhou" 2) "48.3662" 2) 1) "shenzhen" 2) "108.6930" 127.0.0.1:6379> georadius myMap 112.89262 22.90026 200 km withdist count 1 #只获取一个 1) 1) "guangzhou" 2) "48.3662"
2.5 GEORADIUSBYMEMBER(georadiusbymember)
时间复杂度:O(N+log(M))其中,N为以圆心和半径划定的圆形区域的边框内的元素数量,M为索引内的项目数量。
这个命令和 GEORADIUS 命令一样, 都可以找出位于指定范围内的元素, 但是 GEORADIUSBYMEMBER 的中心点是由给定的位置元素决定的, 而不是像 GEORADIUS那样, 使用输入的经度和纬度来决定中心点。
案例:
127.0.0.1:6379> georadiusbymember myMap shenzhen 200 km # 获取深圳为中心200km范围内的城市 1) "shenzhen" 2) "guangzhou" 127.0.0.1:6379> georadiusbymember myMap beijing 200 km #和上面同理 1) "beijing" 2) "tianjing" 127.0.0.1:6379>
2.6 GEOHASH(geohash)
这个基本用不到,知道有这么回事就行
时间复杂度:O(log(N))对于请求的每个成员,其中N是排序集中的元素数量。
返回一个或多个位置元素的 Geohash 表示。
案例:
127.0.0.1:6379> geohash myMap chongqing tianjing # 获取指定位置的 geohash字符串 1) "wm5z22s7520" 2) "wx53vqq7t00" 127.0.0.1:6379>
三、Geospatial存储原理案例
前面我说过,Geospatial是使用Sorted Set存储的,那么我们现在回头想想,上面介绍的六种操作命令有将城市的数据移除的命令吗?
显然没有,那么我现在就用Sorted Set中相关的命令来操作一下
案例:
127.0.0.1:6379> zrange myMap 0 -1 #获取myMap的所有值 1) "chongqing" 2) "shenzhen" 3) "guangzhou" 4) "shanghai" 5) "tianjing" 6) "beijing" 127.0.0.1:6379> zrange myMap 0 -1 withscores #获取myMap的所有值及对应的分数 1) "chongqing" 2) "4026046519194590" 3) "shenzhen" 4) "4046340107163728" 5) "guangzhou" 6) "4046534010880445" 7) "shanghai" 8) "4054807796443227" 9) "tianjing" 10) "4069256193403282" 11) "beijing" 12) "4069896088584598" 127.0.0.1:6379> zrem myMap shenzhen #移除指定的值 (integer) 1 127.0.0.1:6379> zrange myMap 0 -1 withscores 1) "chongqing" 2) "4026046519194590" 3) "guangzhou" 4) "4046534010880445" 5) "shanghai" 6) "4054807796443227" 7) "tianjing" 8) "4069256193403282" 9) "beijing" 10) "4069896088584598" 127.0.0.1:6379>
通过上面这案例也说明了Geospatial的存储结构就是Sorted Set。
参考资料
资料一:《redis系列之——数据类型geospatial:你隔壁有没有老王?》
资料二:Redis中文网
由于博主才疏学浅,难免会有纰漏,假如你发现了错误或偏见的地方,还望留言给我指出来,我会对其加以修正。
如果你觉得文章还不错,你的转发、分享、点赞、留言就是对我最大的鼓励。
感谢您的阅读,十分欢迎并感谢您的关注。
好了,今天的内容到这里就结束了,关注我,我们下期见
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^