news 2026/4/3 4:28:51

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Redis GEO(地理坐标使用Redis存贮及其运用)

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

Redis GEO 是 Redis 3.2 版本引入的地理位置功能,基于 Sorted Set(ZSET) 实现。它允许存储地理位置坐标(经纬度),并进行距离计算、范围查询等操作。
核心数据结构
底层实现:Geohash + Sorted Set

Geohash 编码:将二维的经纬度转换为一维字符串,保留空间邻近性

Sorted Set:使用 64 位整数 score 存储 Geohash 编码

成员(member):地点标识

分值(score):Geohash 的 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 "北京" "上海" km

3. GEOPOS - 获取坐标

# 语法
GEOPOS key member [member ...]

# 示例:获取北京的坐标
GEOPOS cities "北京"
# 返回:116.39712822437286377 39.91652735625285437

Redis 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 member

2. 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 WITHHASH

5. 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位:±2500km
2位:±630km
3位:±78km
4位:±20km
5位:±2.4km
6位:±610m
7位:±76m
8位:±19m
9位:±2.4m
10位:±0.6m
11位:±0.074m

2. Sorted Set 存储结构

// Redis 内部表示
typedef struct zset {
dict *dict; // 哈希表,用于O(1)查找成员
zskiplist *zsl; // 跳表,用于范围查询
} zset;

// GEO 在 ZSET 中的存储
key = "cities"
member = "北京"
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个方向的边界框 Geohash
4. 在 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 cities

2. 实现地理围栏

# 用户签到场景
# 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 ASC

2. 内存优化

# 定期清理过期数据
# 方法1:使用时间戳作为 score 的一部分
GEOADD locations:20240101 116.397 39.916 "user_123"

# 方法2:使用 ZREMRANGEBYSCORE 清理
# 假设 score 包含时间戳
ZREMRANGEBYSCORE locations:20240101 0 1704067200000

3. 集群优化

# 使用 hash tag 确保相关数据在同一节点
# 所有北京的数据在同一个 slot
GEOADD {cities}:beijing 116.397128 39.916527 "location1"
GEOADD {cities}:beijing 116.398 39.917 "location2"

# 查询时也要使用相同的 hash tag
GEOSEARCH {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_001"
GEOADD 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:beijing
geo:users:online
geo:vehicles:active

2. 数据过期策略

# 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 "point1"
GEOADD geo:points 121.473 31.230 "point2"

4. 集群部署注意事项

相同查询的成员应放在同一slot(使用hash tag)

# 使用{}确保相关数据在同一节点
GEOADD geo:{city}:stores 116.397 39.916 "store1"
GEOADD geo:{city}:stores 116.398 39.917 "store2"

与其他方案对比

特性Redis GEOMongoDB 2dspherePostGIS
数据结构Sorted SetGeoJSONGeometry类型
查询类型半径/矩形丰富的地理查询最完整
性能极高中等
部署简单中等复杂
适用场景实时查询、简单地理围栏文档+地理位置复杂GIS分析

https://blog.csdn.net/m0_75259372/article/details/157697082

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/13 11:25:09

服务限流,我有6种实现方式…

服务限流&#xff0c;是指通过控制请求的速率或次数来达到保护服务的目的&#xff0c;在微服务中&#xff0c;我们通常会将它和熔断、降级搭配在一起使用&#xff0c;来避免瞬时的大量请求对系统造成负荷&#xff0c;来达到保护服务平稳运行的目的。下面就来看一看常见的6种限流…

作者头像 李华
网站建设 2026/3/13 4:37:15

单神经元PID控制器在电加热炉炉温控制中的应用设计

单神经元PID控制器在电加热炉炉温控制中的应用设计 第一章 系统设计目标与核心需求 本设计将单神经元PID控制器应用于电加热炉炉温控制&#xff0c;核心目标是解决传统PID控制器在炉温控制中参数整定复杂、抗干扰能力弱、难以适配炉体热惯性滞后特性的问题&#xff0c;实现电加…

作者头像 李华
网站建设 2026/4/3 4:14:46

“不坑盒子”:一个Office插件,如何成为教师数字化转型的“轻骑兵”?

在轰轰烈烈的教育数字化浪潮中&#xff0c;动辄千万级预算的“智慧校园”平台与宏大的顶层设计常成为焦点。然而&#xff0c;在江苏宿迁一所普通中学的教师办公室里&#xff0c;数学老师李老师只需在Word中轻轻一点&#xff0c;便完成了过去需要耗费半天的手工分班、排座与信息…

作者头像 李华
网站建设 2026/3/14 23:21:15

java+vue基于springboot的文化旅游服务系统 小程序系统

目录系统概述技术架构核心功能创新点应用价值开发技术源码文档获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01;系统概述 基于SpringBoot和Vue的文化旅游服务小程序系统&#xff0c;整合了后端Java技术与前端Vue框架&#xff0c;旨在为用户提供便…

作者头像 李华