Redis GEO命令介绍


Redis 在 3.2版本之后,支持GEO类型的数据存储。可以计算两个经纬度之间的距离,也就是,可以利用GEO功能,实现类似滴滴打车附近的车辆,类似微信附近的人基于地理位置的功能。也可以计算两个城市之间的距离,两个位置之间的距离等。

Redis GEO API

下面利用Redis GEO功能,实现一个类似滴滴打车查找附近的车功能。假设我们有2辆车,编号分别为1、2,我们需要将这2辆车当前的经纬度都添加到Redis中。

增加地理位置信息

geoadd key longitude latitude member [longitude latitude member ...]

eg:
向key为cars:locations添加车辆信息

geoadd cars:locations 120.346111 31.556381 1   120.375821 31.560368 2

获取地理位置信息

geopos key name [name ...]

eg: 获取2辆车的位置信息

geopos cars:locations 1 2

1) 1) "120.34611314535140991"
   2) "31.55637987511895659"
2) 1) "120.37582129240036011"
   2) "31.5603669915025975"

获取两个地理位置的距离

在拥有了地理数据之后, 我们就可以基于这些数据进行各种各样的操作。 针对地理位置信息的其中一个最简单的操作, 就是计算两个位置之间的距离。

在 Redis 里面, 计算两个位置之间的距离可以通过 GEODIST 命令来实现:

geodist location-set location-x location-y [unit]

eg:

geodist cars:locations 1 2

"2850.3519"

所以两地相隔 2850.3519 米

可选参数 unit 用于指定计算距离时的单位, 它的值可以是以下单位的其中一个:

  • m 米,默认单位
  • km 千米
  • mi 英里
  • ft 英尺

获取指定范围内的元素

除了计算两地的距离之外, 另一个常见的地理位置操作就是找出特定范围之内的其他存在的地点。 比如找出地点 x 范围 100 米之内的所有地点, 找出地点 y 范围 50 公里之内的所有地点等等。

以给定的经纬度为中心, 返回键包含的位置元素当中, 与中心的距离不超过给定最大距离的所有位置元素。

georadius location-set longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [ASC|DESC] [COUNT count]

georadiusbymember location-set location radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [ASC|DESC] [COUNT count]

georadius和georadiusbymember 两个命令来实现查找特定范围内地点的功能, 它们的作用一样, 只是指定中心点的方式不同

  • georadius 使用用户给定的经纬度作为计算范围时的中心点
  • georadiusbymember 使用储存在位置集合里面的某个地点作为中心点

这两个命令的各个参数的意义如下:

  • m|km|ft|mi 指定的是计算范围时的单位。
  • 如果给定了可选的 withcoord  , 那么命令在返回匹配的位置时会将位置的经纬度一并返回。
  • 如果给定了可选的 withdist  , 那么命令在返回匹配的位置时会将位置与中心点之间的距离一并返回。
  • 如果给定了可选的 withhash , 那么命令在返回匹配的位置时同时返回Redis内部的GeoHash值(非标准算法值),一般用于debug。
  • 在默认情况下, georadius georadiusbymember 的结果是未排序的, asc 可以让查找结果根据距离从近到远排序, 而 desc 则可以让查找结果根据从远到近排序。
  • count 参数指定要返回的结果数量。

eg:
以经度120.375821纬度31.556381为中心查找5公里范围内的车辆

georadius cars:locations 120.375821 31.556381 5 km withcoord asc count 10

1) 1) "2"  							# 符合条件的member
   2) 1) "120.37582129240036011"	# member的经纬度
      2) "31.5603669915025975"
2) 1) "1"
   2) 1) "120.34611314535140991"
      2) "31.55637987511895659"
georadius cars:locations 120.375821 31.556381 5 km withdist asc count 10

1) 1) "2"			# 符合条件的member
   2) "0.4433"		# 与120.375821 31.556381的距离
2) 1) "1"
   2) "2.8157"
georadius cars:locations 120.375821 31.556381 5 km withhash asc count 10

1) 1) "2"							# 符合条件的member
   2) (integer) 4054421167795118	# GeoHash值
2) 1) "1"
   2) (integer) 4054421060663027

可以一起使用

georadius cars:locations 120.375821 31.556381 5 km withdist withcoord asc count 10

1) 1) "2"
   2) "0.4433"
   3) 1) "120.37582129240036011"
      2) "31.5603669915025975"
2) 1) "1"
   2) "2.8157"
   3) 1) "120.34611314535140991"
      2) "31.55637987511895659"

使用1号车的位置为中心查看附近的车

georadiusbymember cars:locations 1 5 km withcoord withdist asc count 10

1) 1) "1"
   2) "0.0000"
   3) 1) "120.34611314535140991"
      2) "31.55637987511895659"
2) 1) "2"
   2) "2.8504"
   3) 1) "120.37582129240036011"
      2) "31.5603669915025975"

Redis GEO背后的原理

Redis 在存储数据不同数据类型的数据时都有对应的编码方式。 Redis GEO是采用哪种编码方式进行存储的呢?

在翻阅Redis GEO API时发现其并没有删除指令,因为其底层是使用zset进行实现的。 我们可以使用zrem 进行数据的删除。

zrange cars:locations 0 -1 withscores

1) "1"
2) "4054421060663027"
3) "2"
4) "4054421167795118"

至此可以推断出Redis GEO 添加经、纬度位置信息的指令的过程是

zadd cars:locations 4054421060663027 1

4054421060663027为对经纬度进行编码后的值。使用4054421060663027作为score 可以快速实现对经纬度的索引。

结尾

分析完Redis GEO的实现原理后不然发现其背后核心是geohash,使用geohash将二维的经纬度数据编码成一维数据,再使用B树索引快速查找出需要的数据。

参考文章: 阿里云Redis GEO地理位置功能上线啦

#中间件


Author: Re:0
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint policy. If reproduced, please indicate source Re:0 !
 Previous
Redis 实现用户签到功能 Redis 实现用户签到功能
签到功能对应的逻辑很常见,主要有以下几种场景 签到 1 天送 10 积分,连续签到 2 天送 20 积分,3 天送 30 积分,4 天以上均送 50 积分等 如果连续签到中断,则重置计数,每月初重置计数 在日历控件上展示用户每月签到情况,
2022-05-31
Next 
架构设计原则 架构设计原则
从程序员到架构师,需要跨域一个鸿沟“不确定性”。在开发中,写出来的代码执行结果是确定的,但是对于架构来说,本质就是不确定的。同样的系统,A公司和B公司架构差异很大,但是都能运行。同一个方法,A方案也能做,B方案也能做,但是各有各的道理。相比
2022-04-28
  TOC