做业务开发时,我们经常遇到这类需求:
✅ 同城筛选附近门店、外卖骑手匹配
✅ 社交app附近的人、同城动态
✅ 网约车就近派单、地理位置打卡
面对这些地理位置检索场景,无需引入笨重的专业gis引擎,redis自带的geo地理空间索引是一个轻量、高性能的方案。
redis geo
redis geo 没有创造新结构,完全基于有序集合 sorted set(zset)实现,底层核心组合:geohash 编码 + sorted set 索引。
简单来说就是:把二维的经纬度坐标,通过算法压缩成一维整数分值,利用zset天然的排序、范围检索能力,实现高效的地理位置查询。
- key:地理集合名(如
stores:location门店位置集合) - member:位置唯一标识(店铺id、用户id、设备id)
- score:经纬度编码后的52位geohash整数(排序、检索的核心)
geohash 编码
geohash的核心价值:将二维经纬度,转化为可排序、可比对的一维字符串。
相近的地理位置,会拥有高度相似的编码前缀,这也是“附近查询”能够高效实现的根本原因。
geohash编码流程:
- 区间划分:针对地球经度范围
[-180, 180]与纬度范围[-90, 90],开展持续的二分操作 - 二进制编码:在每次二分过程中,依据目标点所处区间(左区间记为0,右区间记为1)生成对应的二进制位,且经度与纬度的编码过程交替进行
- base32编码:将生成的二进制数据流,按每5位为一组转换为base32字符
核心命令
| 命令 | 作用 |
|---|---|
geoadd key longitude latitude member [...] | 添加一个或多个地理位置 |
geopos key member [...] | 获取指定成员的经纬度信息 |
geodist key member1 member2 [unit] | 计算两个成员之间的直线距离 |
geohash key member [...] | 获取成员对应的geohash编码,主要用于调试场景 |
geosearch key frommember member byradius radius unit [...] | 以已存储的成员为中心,检索指定圆形区域内的位置 |
geosearch key fromlonlat lng lat byradius radius unit [...] | 以给定的经纬度为中心,检索指定圆形区域内的位置 |
geosearch key fromlonlat lng lat bybox width height unit [...] | 检索指定矩形区域内的位置 |
georadius key fromlonlat lng lat bybox width height unit [...] | 检索指定矩形区域内的位置,该命令已废弃 |
实战演练:实现「附近店铺」功能
将店铺位置录入redis
将店铺的经纬度信息添加至redis数据库
geoadd storeid:location 116.48 39.95 store:1001 geoadd storeid:location 116.50 39.96 store:1002 geoadd storeid:location 116.30 39.90 store:1003
💡 该操作支持批量添加,可一次性录入多个位置信息,能有效提升操作性能。
检索附近的店铺
当用户a位于(116.48, 39.95)时,若需查询5公里范围内的店铺,可执行如下操作:
geosearch storeid:location \ fromlonlat 116.48 39.95 \ # 以指定经纬度为中心 byradius 5 km \ # 搜索半径5公里 withdist \ # 返回距离中心点的距离 withcoord \ # 返回目标的经纬度 count 20 asc # 最多20条,由近及远排序 ## redis 6.2 以下旧版本,可使用以下命令 georadius storeid:location 116.48 39.95 5 km withcoord
java代码
@service
public class locationservice {
@autowired
private redistemplate<string, string> redistemplate;
private static final string geo_key = "storeid:location";
public void updatelocation(long storeid, double lng, double lat) {
// 先删旧位置,再添加新位置
redistemplate.opsforgeo().remove(geo_key, storeid.tostring());
redistemplate.opsforgeo().add(geo_key, new point(lng, lat), storeid.tostring());
}
public georesults<redisgeocommands.geolocation<string>>
findnearby(double lng, double lat, double radius) {
circle circle = new circle(new point(lng, lat),
new distance(radius, metrics.kilometers));
return redistemplate.opsforgeo().radius(geo_key, circle,
redisgeocommands.georadiuscommandargs.newgeoradiusargs()
.includedistance()
.includecoordinates()
.sortascending()
.limit(20));
}
}到此这篇关于redis geo实现地理位置检索功能的文章就介绍到这了,更多相关redis geo位置检索内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论