Redis Geo地理位置与附近的人实现
请介绍Redis中Geo(地理空间)数据结构的底层实现原理(GeoHash编码),以及如何用Geo实现'附近的人'功能。GeoHash映射到ZSet的Score是如何计算的?
回答
编译有声
Geo底层原理:
- Redis Geo本质是对ZSet的封装
- 使用GeoHash算法将二维经纬度编码为一维字符串
- GeoHash的特点:
- 将地球划分为网格,每个网格有唯一的二进制编码
- 编码越长,网格越精细(精度越高)
- 编码前缀相同表示位置相邻(空间局部性)
- 支持字符串前缀匹配来找附近区域
核心命令:
GEOADD city:locations 116.397128 39.916527 "天安门"
GEODIST city:locations "天安门" "王府井" km -- 计算距离
GEOPOS city:locations "天安门" -- 获取经纬度
GEOHASH city:locations "天安门" -- 获取GeoHash字符串
GEORADIUS city:locations 116.39 39.91 5 km -- 以经纬度为中心搜索
GEORADIUSBYMEMBER city:locations "天安门" 5 km -- 以成员为中心搜索
GeoHash映射到ZSet Score:
- Redis将GeoHash二进制编码作为long型整数(52位)
- 直接作为ZSet的Score存储
- 所以具备ZSet的有序性,支持范围查询(GEORADIUS通过Score范围实现)
'附近的人'实现:
// 1. 用户上传位置
redisTemplate.opsForGeo()
.add("user:location", new Point(lng, lat), userId);
// 2. 搜索附近用户
GeoResults<GeoLocation<String>> results = redisTemplate.opsForGeo()
.radius("user:location",
new Circle(new Point(userLng, userLat),
new Distance(5, RedisGeoCommands.DistanceUnit.KILOMETERS)),
GeoRadiusCommandArgs.newGeoRadiusArgs()
.includeDistance()
.includeCoordinates()
.sortAscending()
.limit(20));
// 3. 遍历结果
for (GeoResult<GeoLocation<String>> result : results) {
result.getContent().getName(); // 用户ID
result.getDistance().getValue(); // 距离
}
注意事项:Geo精度在ZSet保存中约在米级,适用于LBS类功能。