文章中如果有图看不到,可以点这里去 csdn 看看。从那边导过来的,文章太多,没法一篇篇修改好。

Redis GEO 52 位整数的经纬分布


在 Redis 的 GEO 实现中,经度和纬度的编码方式是 交错存储而不是分离存储的。在 52 位的 Geohash 编码中,经度(longitude)和纬度(latitude)的比特位是 交替排列的,而不是物理上分开的区块。

具体结构如下:

Geohash 编码的位排列规则

位位置(从最高位开始)012345051
对应坐标经度纬度经度纬度经度经度纬度
  • 偶数索引位(0,2,4,…,50):存储经度信息(共 26 位)
  • 奇数索引位(1,3,5,…,51):存储纬度信息(共 26 位)

编码过程原理(源码解析)

Redis 的 Geohash 编码实现在 geohash.c 文件中,核心逻辑在 geohashEncode() 函数中:

// redis/src/geohash.c
void geohashEncode(...) {
    // ...
    for (i = 0; i < step; i++) {
        double lat_offset, lng_offset;
        uint64_t bits = 0;
        
        // 交替处理经度和纬度
        if (i % 2 == 0) {
            // 偶数位:处理经度
            lng_offset = (lng_range[0] + lng_range[1]) / 2;
            if (longitude >= lng_offset) {
                bits |= 1;
                lng_range[0] = lng_offset;
            } else {
                lng_range[1] = lng_offset;
            }
        } else {
            // 奇数位:处理纬度
            lat_offset = (lat_range[0] + lat_range[1]) / 2;
            if (latitude >= lat_offset) {
                bits |= 1;
                lat_range[0] = lat_offset;
            } else {
                lat_range[1] = lat_offset;
            }
        }
        hash->bits = (hash->bits << 1) | bits;
    }
}

为什么采用交错存储?

  1. 空间局部性保留
    • 经纬度交错排列保持了地理位置的连续性
    • 相邻地理位置的 Geohash 值会有更长的公共前缀(临界特性)
  2. 范围查询优化
// redis/src/geo.c
void georadiusGetAreasByShape(geoRange *r) {
    // 范围查询时会自动对齐Z阶曲线
    geohashBoundingBox(r->longitude, r->latitude, r->radius);
}

这种排列使得经度/纬度联合查询可以直接通过位操作实现

  1. 与Z阶曲线的兼容
    交错存储本质上实现了莫顿码(Morton Code),将二维坐标映射到一维曲线时能最小化空间跳跃

解码过程

当需要反向获取坐标时,Redis 会分离这些位:

// redis/src/geohash.c
int decodeGeohash(uint64_t bits, double *xy) {
    double lat_range[] = {-90, 90};
    double lng_range[] = {-180, 180};
    
    // 交替处理52位
    for (int i = 51; i >= 0; i--) {
        uint64_t bit = (bits >> i) & 1;
        if (i % 2 == 0) { // 经度位
            double mid = (lng_range[0] + lng_range[1]) / 2;
            if (bit) lng_range[0] = mid;
            else lng_range[1] = mid;
        } else { // 纬度位
            double mid = (lat_range[0] + lat_range[1]) / 2;
            if (bit) lat_range[0] = mid;
            else lat_range[1] = mid;
        }
    }
    xy[0] = (lng_range[0] + lng_range[1]) / 2;
    xy[1] = (lat_range[0] + lat_range[1]) / 2;
}

精度说明

坐标轴比特位数理论精度
经度26 bits约 0.0000429° (赤道约 4.78m)
纬度26 bits约 0.0000215° (约 2.39m)

这种交错编码方式虽然增加了编解码的复杂性,但使 Redis 的 GEO 查询性能达到了 O(log N) 的优异水平,是空间与时间的完美平衡。

posted @ 2025-06-11 11:29  NeoLshu  阅读(4)  评论(0)    收藏  举报  来源