介绍
在我们的日常生活中,我们越来越依赖于位置服务(Location-Based Service, LBS),例如查找附近的商场、餐厅,或者在出行时使用打车软件。这些服务都离不开精确的地理位置信息。
LBS应用主要是根据目标人物或物体相关的一组经纬度等地理位置信息,然后根据这些经纬度数据查询目标附近的位置。
Redis GEO 就是专门用于存储和操作地理位置信息的数据类型,它是 Redis 3.2 版本新增的功能,提供了高效的方式来处理和查询这些地理位置信息。
内部实现
Redis GEO 直接使用了 Sorted Set 数据结构来实现其功能,而没有设计新的底层数据结构。它通过 GeoHash 编码将经纬度转换为 Sorted Set 中的元素权重分数。这一过程涉及两个关键机制:
- 将二维空间划分为区间
- 对这些区间进行编码。
具体来说,Redis GEO 首先将地球表面划分为多个区间。每个经纬度点会被映射到一个特定的区间,然后用该区间的编码值作为该点在 Sorted Set 中的权重分数。这样,经纬度信息被转换为有序的权重分数,存储在 Sorted Set 中。
通过这种方式,Redis GEO 能够利用 Sorted Set 的按权重有序范围搜索功能,来实现位置服务中常见的“附近搜索”需求。
关于GeoHash 编码,后面会有文章专门介绍。
常用命令
添加地理位置
geoadd key longitude latitude member [longitude latitude member ...]
# member:位置的唯一标识符,如名称或ID。
此命令支持一次添加一个或多个位置。
查询位置信息
geopos key member [member ...]
此命令支持查看一个或多个位置信息。
距离计算
geodist key member1 member2 [unit]
查询给定经纬度坐标附近的地点
georadius key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC]
# longitude:查询点的经度。
# latitude:查询点的纬度。
# radius:查询的半径距离
# m|km|ft|mi:指定距离的单位(ft:英尺, mi:英里)
# WITHCOORD:在返回结果中包含匹配地点的经纬度坐标
# WITHDIST:在返回结果中包含匹配地点与查询点的距离
# WITHHASH:返回用于排序的 52 位 Geohash 值
# COUNT count:限制返回的结果数量。例如,COUNT 5 将返回最近的 5 个地点。
# ASC|DESC:指定结果按距离的升序(ASC)或降序(DESC)排序
查询位置在地理空间中的 Geohash 值
geohash key **member** [**member** ...]
删除地理位置
zrem key member [member ...]
接下来我们通过一个小例子来看看这些命令的使用。
基本使用
1. 添加地理位置
现在我们有三个地址及其对应的经纬度信息:
- 地铁站:经度 114.16925398121207,纬度 22.31933370421187
- 酒店:经度 114.16797226485401,纬度 22.318558441758913
- 公园:经度 114.16563066766174,纬度 22.31422601342281
接下来,我们可以将这些位置数据添加到 Redis 的 GEO 类型中。通过 Redis GEO,我们能够将经纬度信息存储起来,并使用其强大的查询功能来实现位置相关的操作。
127.0.0.1:6379> geoadd site 114.16934055417407 22.319256344571457 Subway:Station
(integer) 1
127.0.0.1:6379> geoadd site 114.16797226485401 22.318558441758913 Hotel
(integer) 1
127.0.0.1:6379> geoadd site 114.16563066766174 22.31422601342281 Park
(integer) 1
127.0.0.1:6379>
2. 查询位置信息
查看刚刚添加的酒店的地理位置。
127.0.0.1:6379> geopos site Hotel
1 ) 1 ) “114.1679719090461731”
2 ) “22.31855819325424051”
3. 距离计算
我们来算一下从酒店到公园的距离是多少米
127.0.0.1:6379> geodist site Hotel Park m
"538.5748"
可以看出,两个位置之间的直接距离为538.5748米。
4. 查询指定经纬度附近范围内的地址信息
我们来查看一下指定经纬度附近范围内的地址信息,假设以酒店的地址作为输入,看看500米以内的结果:
127.0.0.1:6379> georadius site 114.16797226485401 22.318558441758913 500 m
1) "Hotel"
2) "Subway:Station"
可以看到,地铁站距离酒店不到500米。
以下是一些可选参数的解释:
- WITHCOORD: 在返回结果中包含匹配地点的经纬度坐标
示例:
127.0.0.1:6379> georadius site 114.16797226485401 22.318558441758913 500 m withcoord
1) 1) "Hotel"
2) 1) "114.1679719090461731"
2) "22.31855819325424051"
2) 1) "Subway:Station"
2) 1) "114.16933983564376831"
2) "22.31925524157305318"
- WITHDIST: 在返回结果中包含匹配地点与查询点的距离。
示例:
127.0.0.1:6379> georadius site 114.16797226485401 22.318558441758913 500 m withdist
1) 1) "Hotel"
2) "0.0459"
2) 1) "Subway:Station"
2) "160.6462"
- WITHHASH: 返回用于排序的 52 位 Geohash 值
示例:
127.0.0.1:6379> georadius site 114.16797226485401 22.318558441758913 500 m withhash
1) 1) "Hotel"
2) (integer) 4046428744548977
2) 1) "Subway:Station"
2) (integer) 4046428745401866
- COUNT count: 限制返回的结果数量。
示例:
127.0.0.1:6379> georadius site 114.16797226485401 22.318558441758913 500 m count 1
1) "Hotel"
127.0.0.1:6379> georadius site 114.16797226485401 22.318558441758913 500 m count 2
1) "Hotel"
2) "Subway:Station"
- ASC|DESC: 指定结果按距离的升序(ASC)或降序(DESC)排序
示例:
127.0.0.1:6379> georadius site 114.16797226485401 22.318558441758913 500 m desc
1) "Subway:Station"
2) "Hotel"
127.0.0.1:6379> georadius site 114.16797226485401 22.318558441758913 500 m asc
1) "Hotel"
2) "Subway:Station"
当然,上面的可选参数也可以一起使用,比如:
127.0.0.1:6379> georadius site 114.16797226485401 22.318558441758913 500 m withdist desc
1) 1) "Subway:Station"
2) "160.6462"
2) 1) "Hotel"
2) "0.0459"
5. 查询指定地址的哈希值
127.0.0.1:6379> geohash site Hotel
1) "wecnvyyx480"
该命令支持查询一个或多个地址的哈希值。
6. 删除地址
127.0.0.1:6379> zrem site Hotel
(integer) 1
//Re-query is null
127.0.0.1:6379> geohash site Hotel
1) (nil)
应用场景
网约出租车
下面我们以网约出租车场景为例介绍如何使用GEO命令实现查找附件的人或附近的出租车等功能。
假设现在有两辆出租车:
- 车辆 1 的 ID 为 520,其位置的经纬度为 (114.17351602367509, 22.324873810295088)。
- 车辆 2 的 ID 为 521,其位置的经纬度为 (114.16907428570298, 22.324223743873898)。
我们可以使用一个名为 taxi:locations 的 GEO 集合来保存所有车辆的地理位置信息。
127.0.0.1:6379> geoadd taxi:locations 114.17351602367509 22.324873810295088 520 114.16907428570298 22.324223743873898 521
(integer) 2
当用户想要查找附近的在线出租车时,应用程序可以使用 Redis 的 GEORADIUS 命令来实现这一功能。
例如,当用户发起叫车请求时,应用程序将执行以下命令,Redis 将根据用户提供的经纬度信息 (114.16848419961286, 22.322288413685154) 作为中心点,查找半径 5 公里范围内的所有出租车,并将这些车辆的信息返回给用户:
127.0.0.1:6379> GEORADIUS taxi:locations 114.16848419961286 22.322288413685154 5 km withdist ASC COUNT 5
1) 1) "521"
2) "0.2235"
2) 1) "520"
2) "0.5920"