Redis GEO(地理坐标使用Redis存贮及其运用)

📅 发布时间:2026/7/7 13:54:04 👁️ 浏览次数:
Redis GEO(地理坐标使用Redis存贮及其运用)
Redis GEO地理坐标使用Redis存贮及其运用Redis GEO 是 Redis 3.2 版本引入的地理位置功能基于 Sorted SetZSET 实现。它允许存储地理位置坐标经纬度并进行距离计算、范围查询等操作。核心数据结构底层实现Geohash Sorted SetGeohash 编码将二维的经纬度转换为一维字符串保留空间邻近性Sorted Set使用 64 位整数 score 存储 Geohash 编码成员member地点标识分值scoreGeohash 的 52 位整数表示主要命令1. GEOADD - 添加地理位置# 语法GEOADD key longitude latitude member [longitude latitude member ...]​# 示例添加北京、上海、广州的坐标GEOADD cities 116.397128 39.916527 北京 121.473701 31.230416 上海 113.264385 23.129112 广州一次可添加多个位置如果成员已存在会更新坐标2. GEODIST - 计算两点距离# 语法GEODIST key member1 member2 [unit]​# 示例计算北京到上海的距离GEODIST cities 北京 上海 km3. GEOPOS - 获取坐标# 语法GEOPOS key member [member ...]​# 示例获取北京的坐标GEOPOS cities 北京# 返回116.39712822437286377 39.91652735625285437Redis GEO 核心指令详解一、核心指令深度解析1. GEOADD - 添加地理位置# 完整语法GEOADD key [NX | XX] [CH] longitude latitude member [longitude latitude member ...]​# 参数说明NX仅添加新成员不更新已存在成员XX仅更新已存在成员不添加新成员CH返回变更数量新增更新默认只返回新增数量​# 示例# 添加北京坐标GEOADD cities 116.397128 39.916527 北京​# 使用NX参数北京已存在则忽略GEOADD cities NX 116.397128 39.916527 北京 121.473701 31.230416 上海​# 使用XX参数只更新北京上海不存在则忽略GEOADD cities XX 116.40 39.92 北京 121.47 31.23 上海​# 使用CH参数查看变更总数GEOADD cities CH 116.397128 39.916527 北京 113.264385 23.129112 广州# 返回2如果北京已存在且坐标不同广州是新增​# 批量添加GEOADD cities 116.397128 39.916527 北京 \121.473701 31.230416 上海 \113.264385 23.129112 广州 \120.155069 30.274084 杭州底层原理将经纬度转换为 52位 Geohash 整数使用 Sorted Set 存储member 作为成员Geohash 作为 score实际存储格式ZADD key geohash_int member2. GEODIST - 计算距离# 语法GEODIST key member1 member2 [m|km|mi|ft]​# 示例# 计算北京到上海的距离千米GEODIST cities 北京 上海 km# 返回1068.2986约1068公里​# 计算北京到广州的距离米GEODIST cities 北京 广州 m# 返回1887002.18约1887公里​# 不指定单位默认返回米GEODIST cities 北京 上海# 返回1068298.5792距离计算原理使用 Haversine 公式 计算大圆距离地球半径6372797.560856米公式distance 2R * arcsin(√(sin²(Δφ/2) cosφ₁·cosφ₂·sin²(Δλ/2)))3. GEOPOS - 获取坐标# 语法GEOPOS key member [member ...]​# 示例# 获取单个城市的坐标GEOPOS cities 北京返回 116.39712822437286377 2) 39.91652735625285437​# 获取多个城市的坐标GEOPOS cities 北京 上海 广州# 返回# 1) 116.39712822437286377 2) 39.91652735625285437# 1) 121.47370129823684692 2) 31.23041559259875756# 1) 113.26438432931900024 2) 23.12911163277030071​# 获取不存在的成员GEOPOS cities 纽约# 返回(nil)精度说明返回的坐标精度比输入的更高这是 Geohash 解码的固有特性实际使用中可以截断到所需精度4. GEORADIUS / GEOSEARCH - 范围查询# GEORADIUS传统语法GEORADIUS cities 116.397128 39.916527 500 km WITHDIST WITHCOORD COUNT 10 ASC# 示例1从成员位置查询GEOSEARCH cities FROMMEMBER 北京 BYRADIUS 500 km WITHDIST WITHCOORD​# 示例2从坐标查询GEOSEARCH cities FROMLONLAT 116.397128 39.916527 BYRADIUS 500 km WITHDIST​# 示例3矩形范围查询GEOSEARCH cities FROMMEMBER 北京 BYBOX 100 100 km WITHDIST​# 示例4带分页和排序GEOSEARCH cities FROMLONLAT 116.397128 39.916527 BYRADIUS 1000 km WITHDIST COUNT 5 ASC​# 示例5获取所有信息GEOSEARCH cities FROMMEMBER 北京 BYRADIUS 300 km WITHCOORD WITHDIST WITHHASH5. GEOHASH - 获取Geohash字符串# 语法GEOHASH key member [member ...]​# 示例GEOHASH cities 北京# 返回1) wx4g0b7xrt0​GEOHASH cities 北京 上海 广州# 返回# 1) wx4g0b7xrt0# 2) wtw3sjt9qs0# 3) ws0e89curg0​# 使用场景可用于前端直接调用地图API二、底层原理深度解析1. Geohash 编码原理编码过程经纬度北京: 116.397128, 39.916527↓二进制交错合并经度二进制: 110100101110000101011...纬度二进制: 101110010111000010101...交错后: 1 1 0 1 0 1 1 0 1 0 1 1 ... (经度、纬度交替)↓Base32编码每5位一组: 11100 - w, 10111 - x, 00100 - 4, ...最终: wx4g0b7xrt0特性前缀匹配相同前缀表示地理位置相近精度与长度关系1位±2500km2位±630km3位±78km4位±20km5位±2.4km6位±610m7位±76m8位±19m9位±2.4m10位±0.6m11位±0.074m2. Sorted Set 存储结构// Redis 内部表示typedef struct zset {dict *dict; // 哈希表用于O(1)查找成员zskiplist *zsl; // 跳表用于范围查询} zset;​// GEO 在 ZSET 中的存储key citiesmember 北京score geohash_to_int64(wx4g0b7xrt0) // 52位整数内存布局------------------- -------------------| ZSET | | 跳表节点 || dict: ptr--------|----| member: 北京 || zsl: ptr--------|----| score: 40698849..|------------------- | level[0]-下一个 |-------------------3. 范围查询算法GEORADIUS 执行步骤1. 计算中心点的 Geohash (52位整数)2. 根据半径计算 Geohash 精度级别- 小半径使用高精度更多位数- 大半径使用低精度较少位数3. 生成8个方向的边界框 Geohash4. 在 Sorted Set 中查询该范围内的成员5. 使用 Haversine 公式精确过滤6. 按距离排序并返回结果时间复杂度O(log(N) M)N是总元素数M是返回元素数跳表查询O(log(N))范围扫描O(M)4. 精度与误差控制误差来源Geohash 边界问题相同前缀可能跨越边界# 解决方案查询8个相邻区域# Redis 内部自动处理三、高级用法示例1. 结合 ZSET 命令操作# 1. 查看所有地理位置成员ZRANGE cities 0 -1# 返回1) 广州 2) 上海 3) 北京​# 2. 按 Geohash 范围查询自定义范围ZRANGEBYSCORE cities 4069880000000000 4069890000000000# 可以获取特定 Geohash 范围的成员​# 3. 删除地理位置ZREM cities 北京​# 4. 获取成员分数Geohash 整数ZSCORE cities 北京# 返回4069884985356763​# 5. 统计数量ZCARD cities2. 实现地理围栏# 用户签到场景# 1. 添加地理围栏GEOADD geofence:store 116.397128 39.916527 store_001​# 2. 用户签到GEOPOS user:location user_123# 返回用户当前位置​# 3. 判断是否在围栏内GEODIST geofence:store store_001 user:location user_123 m# 如果距离 50米允许签到​# 4. 批量检查多个围栏GEORADIUS geofence:all 116.397 39.916 100 m WITHDIST# 返回100米内所有围栏及距离3. 分片策略# 城市级别分片GEOADD cities:beijing 116.397128 39.916527 天安门GEOADD cities:shanghai 121.473701 31.230416 外滩​# 网格分片基于 Geohash 前缀# Geohash 前3位相同的在同一个分片# wx4北京区域、wtw上海区域四、性能优化建议1. 查询优化# 避免大范围查询# 不推荐查询5000公里范围GEOSEARCH cities FROMMEMBER 北京 BYRADIUS 5000 km​# 推荐先估算合理半径GEOSEARCH cities FROMMEMBER 北京 BYRADIUS 50 km COUNT 100​# 使用 COUNT 限制结果集GEOSEARCH key FROMLONLAT 116.397 39.916 BYRADIUS 10 km COUNT 50 ASC2. 内存优化# 定期清理过期数据# 方法1使用时间戳作为 score 的一部分GEOADD locations:20240101 116.397 39.916 user_123​# 方法2使用 ZREMRANGEBYSCORE 清理# 假设 score 包含时间戳ZREMRANGEBYSCORE locations:20240101 0 17040672000003. 集群优化# 使用 hash tag 确保相关数据在同一节点# 所有北京的数据在同一个 slotGEOADD {cities}:beijing 116.397128 39.916527 location1GEOADD {cities}:beijing 116.398 39.917 location2​# 查询时也要使用相同的 hash tagGEOSEARCH {cities}:beijing FROMMEMBER location1 BYRADIUS 10 km实际应用示例场景附近的人/商家搜索# 1. 添加商家位置GEOADD stores 116.403963 39.915119 王府井店 116.40853 39.9155 东单店 116.416337 39.928171 三里屯店​# 2. 用户当前位置查询附近3公里内的商家GEOSEARCH stores FROMLONLAT 116.406 39.915 BYRADIUS 3 km WITHDIST ASC​# 3. 计算用户到具体店面的距离GEODIST stores 王府井店 东单店 km场景车辆轨迹管理# 记录车辆实时位置GEOADD fleet:locations 116.397128 39.916527 car_001GEOADD fleet:locations 121.473701 31.230416 car_002​# 查询某中心点50公里内的所有车辆GEOSEARCH fleet:locations FROMLONLAT 116.4 39.9 BYRADIUS 50 km WITHCOORD性能特点优点高性能查询复杂度 O(log(N) M)N是元素数M是返回数高精度使用 WGS84 坐标系统误差在 0.5m 内灵活查询支持半径查询、矩形查询、距离排序与ZSET兼容可使用所有ZSET命令操作GEO数据局限性数据量限制单节点适合千万级数据更大数据需分片地球范围只能处理有效经纬度-180到180-85到85距离计算采用Haversine公式计算大圆距离球面最佳实践1. 键设计# 业务前缀:数据类型:区域geo:stores:beijinggeo:users:onlinegeo:vehicles:active2. 数据过期策略# GEO本身不支持TTL可结合EXPIRE或使用ZSET命令EXPIRE geo:users:online 3600 # 1小时过期3. 批量操作优化# 使用管道(pipeline)批量添加cat locations.txt | redis-cli --pipe# locations.txt内容GEOADD geo:points 116.397 39.916 point1GEOADD geo:points 121.473 31.230 point24. 集群部署注意事项相同查询的成员应放在同一slot使用hash tag# 使用{}确保相关数据在同一节点GEOADD geo:{city}:stores 116.397 39.916 store1GEOADD geo:{city}:stores 116.398 39.917 store2与其他方案对比特性Redis GEOMongoDB 2dspherePostGIS数据结构Sorted SetGeoJSONGeometry类型查询类型半径/矩形丰富的地理查询最完整性能极高高中等部署简单中等复杂适用场景实时查询、简单地理围栏文档地理位置复杂GIS分析https://blog.csdn.net/m0_75259372/article/details/157697082