Redis GEO 底层实现(结合源码分析)

GEO百科知识2个月前发布 GEO研究员
2,463 0

Redis 的 GEO 相关命令(如 GEOADDGEODISTGEORADIUS)最终都会调用 zset 的操作。例如:

  • GEOADD 实际上是调用了 zadd,但会先对经纬度进行编码。
  • GEORADIUS 会先计算目标区域的 Geohash 范围,然后查询 Sorted Set 中符合条件的成员。

Redis 使用 Geohash 算法将经纬度编码成一个 52 位整数(score),具体逻辑在 geohash.c 中:

// redis/src/geohash.c uint64_t geohashEncodeWGS84(double longitude, double latitude, uint8_t step) { return geohashEncode(/* bounds */ -180, 180, -90, 90, longitude, latitude, step); } 

其中:

  • longitude 范围是 [-180, 180]
  • latitude 范围是 [-90, 90]
  • step 表示 Geohash 的精度(Redis 默认使用 26 位 表示经度,26 位 表示纬度,共 52 位)

GEO 数据在 Redis 中的存储形式:

ZSET: Key: "cities:geo" Member: "Beijing" Score: 942457884123456789 (Geohash 编码后的 52 位整数) 

在 Redis 的 GEO 实现中,纬度范围被限制在 [-85°, 85°],而不是理论上的 [-90°, 90°]。原因如下:

Geohash 使用 Z 阶曲线(Z-order curve) 对二维坐标进行编码。在极地附近(纬度接近 ±90°),经度的变化会导致 Geohash 值剧烈波动,导致:

  • 相邻地理位置的 Geohash 值差异巨大,破坏局部性(locality)。
  • 范围查询(GEORADIUS)效率降低,因为 Geohash 无法有效表示极地区域。
  • 在高纬度地区(如北极/南极附近),墨卡托投影(Mercator Projection) 会导致严重的形变,使得距离计算不准确。
  • Redis 使用 Haversine 公式 计算球面距离,但高纬度地区的计算误差会显著增加。

在 Redis 的 geo.c 中,可以看到对纬度的限制:

// redis/src/geo.c int decodeGeohash(double bits, double *xy) { // ... if (xy[1] > 85.05112878 || xy[1] < -85.05112878) { return 0; // 纬度超出范围 } // ... } 

这里的 85.05112878°Web Mercator 投影 的最大有效纬度(由 arctan(sinh(π)) 计算得出)。

因此,Redis GEO 的实现既利用了 Sorted Set 的高效查询特性,又通过 Geohash 和纬度限制保证了地理计算的准确性和性能。

© 版权声明

相关文章

暂无评论

none
暂无评论...