CodeWalk

Redis Geo地理位置与附近的人实现

作者:编译有声 · 2026-05-30 12:55

请介绍Redis中Geo(地理空间)数据结构的底层实现原理(GeoHash编码),以及如何用Geo实现'附近的人'功能。GeoHash映射到ZSet的Score是如何计算的?

回答

编译有声

Geo底层原理

  • Redis Geo本质是对ZSet的封装
  • 使用GeoHash算法将二维经纬度编码为一维字符串
  • GeoHash的特点:
    1. 将地球划分为网格,每个网格有唯一的二进制编码
    2. 编码越长,网格越精细(精度越高)
    3. 编码前缀相同表示位置相邻(空间局部性)
    4. 支持字符串前缀匹配来找附近区域

核心命令

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类功能。