地理空间
Redis GEO(Geographic)是Redis 3.2引入的地理位置数据结构,支持存储坐标、计算距离、查找附近地点等地理计算功能。
GEO原理
底层实现
Bash
GEO基于有序集合(ZSet)实现:
- 经纬度转换为GeoHash值
- GeoHash作为ZSet的score
- 地点名称作为ZSet的member
- 利用ZSet的范围查询能力
GeoHash编码
Bash
GeoHash原理:
- 将经纬度编码为一维字符串
- 经度范围:-180到180
- 纬度范围:-90到90
- 交替编码经纬度得到二进制串
- 转换为base32字符串
特点:
- 相邻地点GeoHash前缀相似
- 前缀匹配表示地理距离相近
- 支持范围查询附近地点
GeoHash精度
Bash
GeoHash长度与精度对应:
1位:约5000km
2位:约1250km
3位:约156km
4位:约39km
5位:约4.9km
6位:约1.2km
7位:约152m
8位:约38m
9位:约4.8m
Redis使用52位精度(约0.1mm误差)
GEO命令
GEOADD添加地理位置
Bash
# 基本语法
GEOADD key longitude latitude member [longitude latitude member ...]
# 添加单个地点
GEOADD locations 116.404 39.915 "北京"
# 添加多个地点
GEOADD locations 121.474 31.230 "上海"
GEOADD locations 113.264 23.129 "广州"
GEOADD locations 114.057 22.543 "深圳"
# 批量添加
GEOADD locations 116.404 39.915 "北京" 121.474 31.230 "上海" 113.264 23.129 "广州"
GEOPOS获取坐标
Bash
# 获取单个地点坐标
GEOPOS locations "北京"
# 返回:
# 1) 1) "116.403999477"
# 2) "39.914999802"
# 获取多个地点坐标
GEOPOS locations "北京" "上海"
# 返回两组坐标
GEODIST计算距离
Bash
# 基本语法
GEODIST key member1 member2 [unit]
# 计算两点距离(默认米)
GEODIST locations "北京" "上海"
# 返回: "1067378.83"(约1067公里)
# 指定单位
# m: 米
# km: 千米
# mi: 英里
# ft: 英尺
GEODIST locations "北京" "上海" km
# 返回: "1067.37883"
GEORADIUS查询附近地点(已弃用)
Bash
# Redis 6.2已弃用,建议使用GEOSEARCH
GEORADIUS locations 116.404 39.915 500 km WITHDIST
GEOSEARCH查询附近地点(推荐)
Bash
# 基本语法(Redis 6.2+)
GEOSEARCH key [FROMMEMBER member | FROMLONLAT longitude latitude]
[BYRADIUS radius unit | BYBOX width height unit]
[WITHCOORD | WITHDIST | WITHHASH]
[COUNT count [ANY]]
[ASC | DESC]
# 从坐标查询500km范围内的地点
GEOSEARCH locations FROMLONLAT 116.404 39.915 BYRADIUS 500 km WITHDIST
# 返回附近地点及距离
# 从地点查询附近
GEOSEARCH locations FROMMEMBER "北京" BYRADIUS 500 km WITHDIST
# 按距离排序
GEOSEARCH locations FROMMEMBER "北京" BYRADIUS 500 km WITHDIST ASC
# 限制返回数量
GEOSEARCH locations FROMMEMBER "北京" BYRADIUS 500 km COUNT 10
# 矩形范围查询
GEOSEARCH locations FROMLONLAT 116 39 BYBOX 200 200 km WITHDIST
GEOHASH获取GeoHash值
Bash
# 获取地点的GeoHash值
GEOHASH locations "北京"
# 返回: "wx4g0b5"
# 获取多个地点的GeoHash
GEOHASH locations "北京" "上海"
命令速查表
| 命令 | 说明 | 示例 |
|---|---|---|
| GEOADD | 添加地理位置 | GEOADD k lon lat m |
| GEOPOS | 获取坐标 | GEOPOS k m |
| GEODIST | 计算距离 | GEODIST k m1 m2 km |
| GEOSEARCH | 查询附近 | GEOSEARCH k ... |
| GEOHASH | 获取GeoHash | GEOHASH k m |
应用场景
1. 附近的人/好友
Bash
# 存储用户位置
GEOADD user:locations 116.404 39.915 "user:1000"
GEOADD user:locations 116.405 39.916 "user:1001"
GEOADD user:locations 116.406 39.917 "user:1002"
# 查询5km内的用户
GEOSEARCH user:locations FROMMEMBER "user:1000" BYRADIUS 5 km WITHDIST COUNT 20
# 返回附近用户列表及距离
2. 附近门店/商家
Bash
# 存储门店位置
GEOADD store:locations 116.404 39.915 "store:A001"
GEOADD store:locations 116.405 39.916 "store:A002"
# 用户当前位置查询附近门店
user_lon = 116.403
user_lat = 39.914
GEOSEARCH store:locations FROMLONLAT user_lon user_lat BYRADIUS 3 km WITHDIST ASC COUNT 10
3. 打车/配送范围
Bash
# 存储司机位置
GEOADD driver:locations 116.404 39.915 "driver:001"
# 乘客查询附近司机
GEOSEARCH driver:locations FROMLONLAT 116.403 39.914 BYRADIUS 5 km WITHDIST ASC
# 配送范围查询
GEOSEARCH delivery:locations FROMMEMBER "配送点1" BYRADIUS 10 km
4. 地理围栏
Bash
# 检查是否在范围内
# 场馆范围:中心点100米内
venue_lon = 116.404
venue_lat = 39.915
# 用户进入场馆范围
user_lon = 116.4041
user_lat = 39.9151
# 计算距离判断
distance = GEODIST venue:user venue_center user_location m
if distance < 100:
# 用户在场馆内
5. 路线规划辅助
Bash
# 存储路线上的关键点
GEOADD route:001 116.404 39.915 "point:1"
GEOADD route:001 116.405 39.916 "point:2"
# 计算路线总距离
distance1 = GEODIST route:001 "point:1" "point:2" km
distance2 = GEODIST route:001 "point:2" "point:3" km
total = distance1 + distance2
精度与误差
坐标精度
Bash
Redis GEO有效范围:
经度:-180到180
纬度:-85.05112878到85.05112878
超出范围会报错:
GEOADD locations 190 90 "invalid"
# 错误:经度超出范围
距离计算误差
text
使用Haversine公式计算距离:
- 基于地球椭球模型
- 理论误差约0.5%
- 实际应用误差可接受
对于短距离:
- 几百米范围内误差很小
- 长距离可能有几十米误差
GeoHash边界问题
text
GeoHash边界:
- 相邻格子GeoHash前缀可能不同
- 需要查询周围格子补充结果
解决方案:
- 搜索时扩大范围
- 使用多个中心点查询
性能优化
数据量控制
text
# 单个GEO集合控制数据量
# 建议不超过10万条
# 大数据量分片
GEOADD locations:beijing ... # 北京区域
GEOADD locations:shanghai ... # 上海区域
查询优化
text
# 使用COUNT限制返回数量
GEOSEARCH locations ... COUNT 100
# 按距离排序,限制数量
GEOSEARCH locations ... ASC COUNT 20
# 避免查询过大范围
# 范围越大,计算量越大
定期清理
text
# 清理过期数据
# 离线用户位置定期删除
ZREM user:locations "user:offline"
# 或使用过期时间
SET user:location:1000:expire timestamp
底层ZSet操作
直接操作底层ZSet
text
# GEO数据底层是ZSet
# 可使用ZSet命令
# 查看地点数量
ZCARD locations
# 查看所有地点
ZRANGE locations 0 -1
# 删除地点
ZREM locations "北京"
# 获取GeoHash(score)
ZSCORE locations "北京"
注意事项
text
- GEO命令更方便,推荐使用
- ZSet命令可用于批量操作
- score是GeoHash编码值,非经纬度
与其他方案对比
| 特性 | Redis GEO | 专业GIS | 粗略计算 |
|---|---|---|---|
| 精度 | 中 | 高 | 低 |
| 功能 | 基础 | 完善 | 有限 |
| 性能 | 高 | 中 | 高 |
| 成本 | 低 | 高 | 低 |
| 适用 | 简单场景 | 专业应用 | 粗略估算 |
要点总结
- GEO基于ZSet实现,经纬度编码为GeoHash作为score
- GEOADD添加地理位置,GEOPOS获取坐标
- GEODIST计算两点距离,支持m/km/mi/ft单位
- GEOSEARCH查询附近地点,Redis 6.2+推荐使用
- GeoHash前缀相似表示地理位置相近
- 应用场景:附近人、附近门店、打车配送、地理围栏
- 有效经度范围:-180到180,纬度:约-85到85
- 距离计算误差约0.5%,实际可接受
- 单个GEO集合建议不超过10万数据
- 底层是ZSet,可使用ZSet命令批量操作
- 简单地理场景用Redis GEO,专业GIS用专业方案
📝 发现内容有误?点击此处直接编辑