redis之GEO使用

写在前面

在LBS(location based service)应用,如滴滴打车应用,需要根据用户的位置信息来获取某些数据,如获取距离当前用户指定距离范围内的所有车辆信息,该类的应用就可以使用本文我们要学习的GEO了,接下来一起看下。

1:实战

1.1:geoadd

添加位置信息,格式GEOADD key longitude latitude member [longitude latitude member ...],可以指定多组经度+纬度+位置名称,如下:

redis> GEOADD Sicily 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania"
(integer) 2

1.2:geopos

获取指定位置的经纬度,如果是不存在的位置则返回nil,格式GEOPOS key member [member ...],如下测试:

redis> GEOADD Sicily 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania"
(integer) 2
redis> GEOPOS Sicily Palermo Catania NonExisting
1) 1) "13.36138933897018433"
   2) "38.11555639549629859"
2) 1) "15.08726745843887329"
   2) "37.50266842333162032"
3) (nil)
redis>

1.3:geodist

geodist 用于返回两个给定位置之间的距离。geodist 语法格式为GEODIST key member1 member2 [m|km|ft|mi],参数说明如下:

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

如果是不存在的地点则返回nil,如下测试:

redis> GEOADD Sicily 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania"
(integer) 2
redis> GEODIST Sicily Palermo Catania
"166274.1516"
redis> GEODIST Sicily Palermo Catania km
"166.2742"
redis> GEODIST Sicily Palermo Catania mi
"103.3182"
redis> GEODIST Sicily Foo Bar
(nil)

1.4:georadius

以给定的经纬度为中心,并指定距离范围,返回该距离范围内的所有地点信息,格式如下:

GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]

参数说明如下:

m:米,默认单位。
km:千米。
mi:英里。
ft:英尺。
WITHDIST: 在返回位置元素的同时, 将位置元素与中心之间的距离也一并返回。
WITHCOORD: 将位置元素的经度和纬度也一并返回。
WITHHASH: 以 52 位有符号整数的形式, 返回位置元素经过原始 geohash 编码的有序集合分值。 这个选项主要用于底层应用或者调试, 实际中的作用并不大。
COUNT 限定返回的记录数。
ASC: 查找结果根据距离从近到远排序。
DESC: 查找结果根据从远到近排序。

测试如下:

redis> GEOADD Sicily 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania"
(integer) 2
redis> GEORADIUS Sicily 15 37 200 km WITHDIST
1) 1) "Palermo"
   2) "190.4424"
2) 1) "Catania"
   2) "56.4413"
redis> GEORADIUS Sicily 15 37 200 km WITHCOORD
1) 1) "Palermo"
   2) 1) "13.36138933897018433"
      2) "38.11555639549629859"
2) 1) "Catania"
   2) 1) "15.08726745843887329"
      2) "37.50266842333162032"
redis> GEORADIUS Sicily 15 37 200 km WITHDIST WITHCOORD
1) 1) "Palermo"
   2) "190.4424"
   3) 1) "13.36138933897018433"
      2) "38.11555639549629859"
2) 1) "Catania"
   2) "56.4413"
   3) 1) "15.08726745843887329"
      2) "37.50266842333162032"

1.5:geohash

geohash算法是一种将经纬度转换为一个整数值的算法,转换后数值接近的则说明地理位置上更接近,则当我们要按照距离来获取地点时就可以使用了。当使用redis的geohash命令获取经纬度对应的geohash值后,就可以将其作为score存储到底层的zset中了,语法格式为GEOHASH key member [member ...],测试如下:

redis> GEOADD Sicily 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania"
(integer) 2
redis> GEOHASH Sicily Palermo Catania
1) "sqc8b49rny0"
2) "sqdtr74hyu0"

2:原理

我们知道,经度的范围是[-180,180],纬度的范围是[-90,90],我们可以将其想想为如下的一个矩形,矩形中的每个点都有一个经纬度的信息,如下图:

在这里插入图片描述

但是这个图里每一个点目前都是用经度和纬度来表示的,如果是我们能够将矩形划分成很多个区域,然后每个区域使用一个整数来表示,则就很容易方便查找周边经纬度了,而redis就使用GeoHash编码方法实现了这个需求,基本原理是二分区间,区间编码,二分区间的意思是将经度或纬度的范围平分为两份,区间编码就是对每一份给一个编码,具体的,redis会分别对经度和纬度执行GeoHash,然后将二者的编码合并为最终的编码。

GeoHash会将经度和纬度划分为N位的二进制编码,其中N就是需要对区间划分的次数,这里我们假设N为5来编码,假设要编码的经纬度是(116.37,39.86)看下这个过程:

首先对经度编码,如果落在左区间则编码为0,如果落在右区间则编码为1:

二分[-180,180]为[-180,0),[0,180]左右两个区间,116.37落在右区间,所以编码为1
二分[0,180]为[0,90),[90,180]左右两个区间,116.37落在右区间,所以编码为1
二分[90,180]为[90,135),[135,180]左右两个区间,116.37落在左区间,所以编码为0
二分[90,135]为[90,112.5),[112.5,135]左右两个区间,116.37落在右区间,所以编码为1
二分[112.5,135]为[112.5,123.75),[123.75,135]左右两个区间,116.37落在左区间,所以编码为0

所以最终的编码是11010,该过程也可以用下图表示:

在这里插入图片描述
纬度同该编码过程,最终结果是10111,该过程可以用下图表:
在这里插入图片描述
最终二者组合的规则是:从左到右偶数位依次取经度的编码,奇数位依次取纬度的编码,因此我们先来取经度的编码,为1?1?0?1?0?,接着我们将其中的?,即奇数位使用纬度的编码替换,结果就是1110011101,整个过程如下图:
在这里插入图片描述

这样,一个经纬度就会被转换为一个整数,而相近的整数在实际的地理位置上也是更接近的,这里我们生成编码的N如果是越大,则矩形的区域也会被划分的越细,距离的匹配也会更加精确。

其实GEO底层使用的数据结构正是zset,而DeoHash的结果是作为其score的,这样当我们查找某地附近的地点时,就可以直接上前向后直接查找了。

3:打车场景

假定用户打车场景中根据用户所在定位查找车辆,先模拟库中的车辆信息:

127.0.0.1:6379> GEOADD car:location 13.361389 38.115556 "张师傅" 15.087269 37.502669 "李师傅" 17.087269 35.502669 "刘师傅" 117.087269 65.502669 "孙小姐"

现在顾客芒果小朋友要打车,假设经纬度是13.361399 38.115565,则查找其5公里范围内的车辆,如下:

127.0.0.1:6379> GEORADIUS car:location 13.361399 38.115565 5 km
张师傅

可以看到只有张师傅符合要求,那么就可以给张师傅派单了。

写在后面

参考文章列表:

Redis GEO

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值