Redis之Geospatial:你不知道的,附近人

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: Redis之Geospatial:你不知道的,附近人


一、Geospatial概述


Redis 在 3.2 版本以后增加了地理位置 GEO 模块。


生活中我们常常见到的功能如:附近的人、查找共享单车、美团找附近的餐馆等用来指定两地之间的距离基本上都可以用这个来实现!


并且Geospatial本质是使用Sorted Set存储的,当然计算两者之间的距离Redis用的底层技术则是GeoHash(不了解的,可以看我下面贴出来的资料一)。


Redis对GEO模块添加地理位置的形式是:


key 经度 纬度 名称


  • key:这个好理解我就不解释了
  • 经度:从本初子午线0度开始,向左和向右分别分出180度,跨度是(-180,180),其中本初子午线向左称为西经,本初子午线向右称为东经。
  • 纬度:以赤道0度开始,向上和向下分别分出90度,南极和北极分别为南纬90度和北纬90度,南极到北极的跨度是(-90,90),其中赤道到南极称为南纬,赤道到北极称为北纬。
  • 名称:经度与纬度对应的地理名称


注意的点:


  1. 两级无法直接添加。
  2. 经纬度一定不能超出范围,有效的纬度从-85.05112878度到85.05112878度、有效的经度从-180度到180度。
  3. 地球是圆的,这两个数字的单位不是距离,而是角度。


学下面六个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> 


用上面我贴的工具验证一下:


image.png


我们注意到,这计算的还是很精准的。


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中文网


由于博主才疏学浅,难免会有纰漏,假如你发现了错误或偏见的地方,还望留言给我指出来,我会对其加以修正。


如果你觉得文章还不错,你的转发、分享、点赞、留言就是对我最大的鼓励。

感谢您的阅读,十分欢迎并感谢您的关注。


好了,今天的内容到这里就结束了,关注我,我们下期见


^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^


相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
7月前
|
存储 NoSQL 算法
Redis-用户关注、附近商户、用户签到、UV统计
好友关注 关注和取消关注 针对用户的操作:可以对用户进行关注和取消关注功能。 实现思路: 需求:基于该表数据结构,实现两个接口: 关注和取关接口 判断是否关注的接口 关注是User之间的关系,是博主与粉丝的关系,数据库中有一张tb_follow表来标示: 注意: 这里需要把主键修改为自增长,简化开发。 FollowController //关注 @PutMapping("/{id}/{isFollow}") public Result follow(@PathVariable("id") Long followUserId, @PathVariable("isFollow"
40 1
|
3天前
|
NoSQL Java Redis
Spring Boot和Redis Geo实现附近的人【redis实战 三】
Spring Boot和Redis Geo实现附近的人【redis实战 三】
76 0
|
9月前
|
NoSQL Java 定位技术
Spring Boot实战分页查询附近的人: Redis+GeoHash+Lua
Spring Boot实战分页查询附近的人: Redis+GeoHash+Lua
290 0
|
NoSQL Java 定位技术
Redis的三种特殊数据类型Geospatial(地理位置)
Redis的三种特殊数据类型Geospatial(地理位置)
|
NoSQL 定位技术 Redis
【Redis】特殊数据类型 - Geospatial (地理空间)
【Redis】特殊数据类型 - Geospatial (地理空间)
【Redis】特殊数据类型 - Geospatial (地理空间)
|
消息中间件 NoSQL Java
Redis的地理空间(geospatial)
Redis的地理空间(geospatial)
302 0
|
NoSQL Redis
【Redis】实现附近人功能
【Redis】实现附近人功能
74 0
【Redis】实现附近人功能
|
存储 NoSQL 算法
Redis 实战篇:GEO助我邂逅附近女神
多锻炼自己的表达能力,特别是在工作中。很多人说「干活的不如那些做 PPT 的」,实际上老板都不傻,为何他们会更认可那些做 PPT 的? 因为他们从老板的角度考虑问题,对他而言,需要的是一个「解决方案」。多从一个创造者的视角去考虑问题,而不是局限在用程序员的视角考虑问题; 多想一下这个东西到底给人提供什么价值,而不是「我要怎么实现它」。当然,怎么实现是必须的,但通常不是最重要的。
243 0
Redis 实战篇:GEO助我邂逅附近女神
|
NoSQL Java 定位技术
面试突击第 3 期 | Redis 如何实现查询附近的人?视频实战版
面试突击第 3 期 | Redis 如何实现查询附近的人?视频实战版
143 0
面试突击第 3 期 | Redis 如何实现查询附近的人?视频实战版
|
存储 NoSQL 关系型数据库
使用Redis实现附近的人及打车服务(下)
各种社交软件里面都有附件的人的需求,在该应用中,我们查询附近 1 公里的食客,同时只需查询出 20 个即可。 这都依赖基于位置信息服务(Location-Based Service,LBS)的应用。LBS应用访问的数据是和人或物关联的一组经纬度信息,而要能查询相邻的经纬度范围,GEO就非常适合应用在LBS服务的场景。 解决基于地理位置的搜索,很多数据库品牌都支持:MySQL、MongoDB、Redis 等都能支持地理位置的存储。
177 0
使用Redis实现附近的人及打车服务(下)