Redis Geo地理位置功能实现附近商家搜索
Redis 的 Geo 模块基于有序集合(ZSET)实现,底层将经纬度通过 Geohash 算法转换为 52 位的字符串索引。这种机制使得在处理地理位置计算时,依然保持 ZSET 的高效读写性能。以下将分步骤演示如何从零开始构建一个“附近商家”搜索功能。
核心原理简述
Geo 类型本质上是经纬度与成员名称(如商家 ID)的映射。在底层存储时,Redis 将二维的经纬度映射为一维的 52 位整数,作为 ZSET 的 Score。这样,计算“附近”的任务就转化为计算分数范围内的任务,利用 ZSET 的跳跃表实现快速查询。
步骤一:录入商家位置数据
首先需要将商家的经纬度信息存储到 Redis 中。使用 GEOADD 命令可以一次性添加一个或多个地理位置。
- 打开终端或命令行工具。
- 连接到 Redis 服务。
- 输入以下命令向名为
shops的键中添加三个商家:星巴克、麦当劳和肯德基。
GEOADD shops 116.404269 39.915168 starbucks
GEOADD shops 116.407826 39.914936 mcdonalds
GEOADD shops 116.401394 39.913942 kfc
参数说明:
shops:集合的键名。116.404269:经度。39.915168:纬度。starbucks:商家标识(member)。
- 确认数据已添加。可以通过
ZCARD shops查看元素数量。
步骤二:计算两地之间的距离
在用户查看某个商家详情时,通常需要计算该商家与用户当前位置的直线距离。使用 GEODIST 命令即可完成。
- 输入以下命令计算“星巴克”与“肯德基”之间的距离,单位指定为千米。
GEODIST shops starbucks kfc km
- 查看返回结果。如果输出
0.4189,表示两者相距约 0.42 公里。
支持的单位包括:m(米)、km(千米)、mi(英里)、ft(英尺)。
步骤三:查询附近的商家(核心功能)
这是实现“附近搜索”的关键步骤。假设用户当前位于经度 116.405528,纬度 39.915678(天安门附近),需要查找方圆 500 米内的所有商家,并按距离由近及远排序。
- 输入以下命令执行范围查询。
GEORADIUS shops 116.405528 39.915678 500 m WITHDIST WITHCOORD ASC
参数解释:
116.405528 39.915678:中心点经纬度。500 m:半径 500 米。WITHDIST:同时返回距离中心点的直线距离。WITHCOORD:同时返回商家的经纬度。ASC:按距离从近到远排序。
- 观察返回结果。结果通常以数组形式展示,包含商家名称、距离和坐标。
步骤四:优化查询结果
在实际业务中,不需要一次性返回所有几百个附近的商家,通常只需要最近的 5 个或 10 个。可以通过 COUNT 参数限制返回数量,提高传输效率。
- 输入以下命令,仅查找距离最近的 2 家商家。
GEORADIUS shops 116.405528 39.915678 500 m WITHDIST COUNT 2 ASC
- 对比步骤三的结果,此时列表只会包含距离最近的两个元素。
步骤五:业务逻辑流程图
为了更直观地理解数据流向,以下展示了用户发起请求到获取商家列表的完整处理流程。
graph LR
A["用户端: 发送 GPS 坐标"] --> B["API 网关"]
B --> C["应用服务器: 编写 Redis 命令"]
C --> D["Redis: 执行 GEORADIUS"]
D --> E["数据库: 返回排序后的商家列表"]
E --> C
C --> F["应用服务器: 组装 JSON 数据"]
F --> G["用户端: 渲染附近商家列表"]
步骤六:Python 代码实战
以下是一个使用 Python 的 redis-py 库封装的完整示例,模拟了一个简单的 API 逻辑。
- 安装 Redis 客户端库。
pip install redis
- 创建名为
geo_demo.py的文件,并写入以下代码。
import redis
# 1. 连接到 Redis
r = redis.Redis(host='localhost', port=6379, db=0)
def add_shops():
# 2. 批量添加商家数据
# 格式: mapping = {member: (longitude, latitude)}
shop_locations = {
"starbucks": (116.404269, 39.915168),
"mcdonalds": (116.407826, 39.914936),
"kfc": (116.401394, 39.913942),
"subway": (116.408528, 39.918678)
}
r.geoadd("shops", shop_locations)
print("商家数据已添加。")
def find_nearby_shops(longitude, latitude, radius=1000):
# 3. 查找附近商家
# unit='m' 表示单位为米
# withdist=True 返回距离
# sort='ASC' 按距离升序排列
results = r.georadius(
name="shops",
longitude=longitude,
latitude=latitude,
radius=radius,
unit='m',
withdist=True,
withcoord=True,
sort='ASC',
count=5 # 限制返回5个
)
print(f"当前位置 ({longitude}, {latitude}) 附近 {radius}米 内的商家:")
print("-" * 50)
# 格式化输出结果
for result in results:
name = result[0].decode('utf-8')
distance = round(result[1], 2)
lon = round(result[2][0], 6)
lat = round(result[2][1], 6)
print(f"商家: {name}")
print(f"距离: {distance} 米")
print(f"坐标: [{lon}, {lat}]")
print("-" * 50)
if __name__ == "__main__":
# 初始化数据
add_shops()
# 模拟用户位置(例如:天安门东地铁站附近)
user_lon, user_lat = 116.407526, 39.904030
# 执行搜索
find_nearby_shops(user_lon, user_lat, radius=2000)
- 运行脚本。
python geo_demo.py
- 查看控制台输出的商家列表,程序会自动列出距离指定坐标最近的 5 家商家及其精确距离。
常用命令速查表
下表汇总了开发过程中最常用的命令及其作用。
| 命令名称 | 作用 | 常用参数示例 |
|---|---|---|
GEOADD |
添加地理位置坐标 | GEOADD key lng lat member |
GEODIST |
计算两个成员间距离 | GEODIST key member1 member2 unit |
GEOHASH |
获取成员的 Geohash 字符串 | GEOHASH key member |
GEOPOS |
获取成员的坐标 | GEOPOS key member |
GEORADIUS |
以坐标为中心查询附近 | GEORADIUS key lng lat radius unit |
GEORADIUSBYMEMBER |
以成员为中心查询附近 | GEORADIUSBYMEMBER key member radius unit |

暂无评论,快来抢沙发吧!