Redis GEO 命令詳解:輕松實現“附近的人“功能

目錄

引言

Redis GEO命令概述

什么是GEO命令?

主要命令詳解

命令應用示例

添加地點信息

查詢兩地距離

查詢附近的城市

實現"查找附近的人"功能

功能需求與實現思路

基本需求

實現思路

命令實現方案

存儲用戶位置

查詢附近的用戶

Java代碼實現詳解

使用Redis GEO的優勢與注意事項

優勢

注意事項


引言

????????在移動互聯網時代,基于地理位置的服務已成為眾多應用的標配功能。無論是打車軟件、外賣平臺還是社交應用,"附近的XX"功能幾乎無處不在。這類功能的核心技術挑戰在于:如何高效存儲地理位置數據并進行快速檢索?Redis 3.2版本引入的GEO(地理空間)命令集完美解決了這一問題,為開發者提供了簡單高效的地理位置數據處理方案。

????????本文將深入淺出地介紹Redis GEO命令及其工作原理,通過實際案例和代碼示例,幫助你輕松實現"查找附近的人"等地理位置相關功能。無論你是Redis新手還是有經驗的開發者,都能從中獲取有價值的信息。


Redis GEO命令概述

什么是GEO命令?

????????GEO是"Geolocation"(地理定位)的簡寫,Redis GEO是Redis專門為地理位置信息存儲和檢索設計的命令集。它允許我們將經緯度坐標存儲到Redis數據庫中,并支持按距離查詢、計算兩點間距離等多種地理空間操作。

????????底層實現上,Redis GEO使用了地理空間索引算法(Geohash),將二維的經緯度轉換為一維的字符串,并通過Redis的有序集合(Sorted Set)來存儲,這使得地理位置的存取和計算變得非常高效。

主要命令詳解

Redis GEO主要提供了以下幾個核心命令:

  • GEOADD: 添加地理空間信息
#?將指定的地理空間位置(經度、緯度、名稱)添加到指定的key中
# 可以一次添加多個位置
GEOADD key longitude latitude member [longitude latitude member ...]
  • GEODIST: 計算兩點間距離
# 返回兩個給定位置之間的距離
# unit參數指定返回值的單位,可以是m(米)、km(千米)、mi(英里)或ft(英尺)
GEODIST key member1 member2 [unit]
  • GEOHASH: 獲取經緯度的Geohash表示
# 返回一個或多個位置元素的Geohash表示
# Geohash是一種將經緯度編碼為字符串的方法
GEOHASH key member [member ...]
  • GEOHASH: 獲取經緯度的Geohash表示
# 返回一個或多個位置元素的Geohash表示
# Geohash是一種將經緯度編碼為字符串的方法
GEOHASH key member [member ...]
  • GEOPOS: 獲取位置的經緯度
# 返回指定名稱位置的經緯度坐標
GEOPOS key member [member ...]
  • GEORADIUS: 查找指定半徑內的成員
# 以給定的經緯度為中心,返回鍵中包含的位置元素當中,與中心的距離不超過給定半徑的所有位置元素
GEORADIUS key longitude latitude radius unit [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]

WITHDIST: 在返回位置元素的同時,將位置元素與中心之間的距離也一并返回
WITHCOORD: 將位置元素的經度和緯度也一并返回
WITHHASH: 以52位無符號整數的形式返回位置元素的geohash值(主要用于調試)
COUNT n: 限定返回的記錄數量
ASC|DESC: 根據中心的位置,按照從近到遠(ASC)或從遠到近(DESC)的順序返回位置元素

  • GEOSEARCH: 在指定范圍內搜索
# 在指定范圍內搜索,范圍可以是圓形或矩形
GEOSEARCH key [FROMMEMBER member] [FROMLONLAT longitude latitude] [BYRADIUS radius unit] [BYBOX width height unit] [WITHDIST] [WITHCOORD] [WITHHASH] [COUNT count] [ASC|DESC]
  • GEOSEARCHSTORE: 在指定范圍內搜索并將結果存儲
# 與GEOSEARCH功能相同,但可以將結果存儲到指定的key中
GEOSEARCHSTORE destination key [FROMMEMBER member] [FROMLONLAT longitude latitude] [BYRADIUS radius unit] [BYBOX width height unit] [WITHDIST] [WITHCOORD] [WITHHASH] [COUNT count] [ASC|DESC]

這些命令共同構成了一個完整的地理空間數據處理工具集,能夠滿足大多數基于位置的服務需求。

命令應用示例

讓我們通過一個具體的例子來理解GEO命令的使用:

添加地點信息

# 上述命令將"東京"和"吉隆坡"兩個城市的經緯度信息添加到名為"locations"的地理空間集合中。
GEOADD locations 139.781210 35.774426 "東京" 101.653962 5.205122 "吉隆坡"

查詢兩地距離

# 這個命令會返回東京和吉隆坡之間的距離(單位:公里)。
GEODIST locations "東京" "吉隆坡" km

查詢附近的城市

# 這個命令會查找距離指定坐標點(經度139.0,緯度35.0)1000公里范圍內的所有城市,并同時返回它們與中心點的距離。
GEORADIUS locations 139.0 35.0 1000 km WITHDIST

實現"查找附近的人"功能

????????"查找附近的人"是移動應用中的常見功能,下面我們將詳細講解如何使用Redis GEO命令來實現。

功能需求與實現思路

基本需求

  • 存儲每個用戶的地理位置信息(經緯度)
  • 能夠查詢指定用戶周圍一定范圍內的其他用戶
  • 返回的用戶列表按照距離排序

實現思路

  1. 使用GEOADD命令將用戶ID及其經緯度信息存儲在Redis中
  2. 當需要查詢"附近的人"時,使用GEORADIUS命令,以查詢用戶的位置為中心,指定半徑范圍進行搜索

命令實現方案

假設我們正在開發一個社交應用,需要實現廣州市用戶查找1000公里范圍內其他用戶的功能:

存儲用戶位置

GEOADD user_location 113.267548 23.142979 "user1"
GEOADD user_location 113.300000 23.150000 "user2"
GEOADD user_location 114.057868 22.543099 "user3"

查詢附近的用戶

# 命令會返回距離廣州市指定坐標1000公里范圍內的所有用戶,并顯示他們與查詢點的具體距離。
GEORADIUS user_location 113.254325 23.144043 1000 km WITHDIST

Java代碼實現詳解

下面是使用Java語言和Jedis客戶端實現"查找附近的人"功能的代碼示例:

import redis.clients.jedis.GeoCoordinate;
import redis.clients.jedis.GeoRadiusResponse;
import redis.clients.jedis.GeoUnit;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.params.GeoRadiusParam;import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.stream.Collectors;/*** Redis GEO功能示例:實現"附近的人"功能* * @author Muller*/
public class RedisGeoDemo {private static final String USER_LOCATION_KEY = "user_location";/*** 存儲用戶地理位置信息* * @param userId 用戶ID* @param longitude 經度* @param latitude 緯度* @param jedis Redis連接* @return 添加成功的數量*/public static Long saveUserLocation(String userId, double longitude, double latitude, Jedis jedis) {try {return jedis.geoadd(USER_LOCATION_KEY, longitude, latitude, userId);} catch (Exception e) {System.err.println("保存用戶位置信息失敗: " + e.getMessage());return 0L;}}/*** 批量存儲多個用戶的地理位置信息* * @param userLocations 用戶位置Map,key為用戶ID,value為經緯度坐標* @param jedis Redis連接* @return 添加成功的數量*/public static Long saveUserLocations(Map<String, double[]> userLocations, Jedis jedis) {try {Map<String, GeoCoordinate> memberCoordinateMap = new HashMap<>();for (Map.Entry<String, double[]> entry : userLocations.entrySet()) {String userId = entry.getKey();double[] coordinates = entry.getValue();memberCoordinateMap.put(userId, new GeoCoordinate(coordinates[0], coordinates[1]));}return jedis.geoadd(USER_LOCATION_KEY, memberCoordinateMap);} catch (Exception e) {System.err.println("批量保存用戶位置信息失敗: " + e.getMessage());return 0L;}}/*** 查詢附近的人* * @param longitude 經度* @param latitude 緯度* @param radius 半徑* @param jedis Redis連接* @return 附近用戶ID列表*/public static List<String> getNearbyUsers(double longitude, double latitude, double radius, Jedis jedis) {try {List<GeoRadiusResponse> responses = jedis.georadius(USER_LOCATION_KEY, longitude, latitude, radius, GeoUnit.KM, GeoRadiusParam.geoRadiusParam().withDist().sortAscending());return responses.stream().map(GeoRadiusResponse::getMemberByString).collect(Collectors.toList());} catch (Exception e) {System.err.println("查詢附近用戶失敗: " + e.getMessage());return List.of();}}/*** 獲取用戶詳細地理信息(包含距離)* * @param longitude 經度* @param latitude 緯度* @param radius 半徑* @param jedis Redis連接* @return 附近用戶詳細信息列表*/public static List<UserGeoInfo> getNearbyUsersWithDistance(double longitude, double latitude, double radius, Jedis jedis) {try {List<GeoRadiusResponse> responses = jedis.georadius(USER_LOCATION_KEY, longitude, latitude, radius, GeoUnit.KM, GeoRadiusParam.geoRadiusParam().withDist().withCoord().sortAscending());return responses.stream().map(response -> new UserGeoInfo(response.getMemberByString(),response.getDistance(),response.getCoordinate().getLongitude(),response.getCoordinate().getLatitude())).collect(Collectors.toList());} catch (Exception e) {System.err.println("查詢附近用戶詳細信息失敗: " + e.getMessage());return List.of();}}/*** 計算兩個用戶之間的距離* * @param userId1 用戶1的ID* @param userId2 用戶2的ID* @param jedis Redis連接* @return 兩用戶間距離(單位:公里),如果計算失敗返回-1*/public static double getDistanceBetweenUsers(String userId1, String userId2, Jedis jedis) {try {Double distance = jedis.geodist(USER_LOCATION_KEY, userId1, userId2, GeoUnit.KM);return distance != null ? distance : -1;} catch (Exception e) {System.err.println("計算用戶距離失敗: " + e.getMessage());return -1;}}/*** 獲取用戶的地理坐標* * @param userId 用戶ID* @param jedis Redis連接* @return 用戶坐標[經度,緯度],如果不存在返回null*/public static double[] getUserPosition(String userId, Jedis jedis) {try {List<GeoCoordinate> positions = jedis.geopos(USER_LOCATION_KEY, userId);if (positions != null && !positions.isEmpty() && positions.get(0) != null) {GeoCoordinate pos = positions.get(0);return new double[] { pos.getLongitude(), pos.getLatitude() };}return null;} catch (Exception e) {System.err.println("獲取用戶坐標失敗: " + e.getMessage());return null;}}/*** 用戶地理信息包裝類*/public static class UserGeoInfo {private String userId;private double distance;private double longitude;private double latitude;public UserGeoInfo(String userId, double distance, double longitude, double latitude) {this.userId = userId;this.distance = distance;this.longitude = longitude;this.latitude = latitude;}public String getUserId() {return userId;}public double getDistance() {return distance;}public double getLongitude() {return longitude;}public double getLatitude() {return latitude;}@Overridepublic String toString() {return "用戶ID: " + userId + ", 距離: " + String.format("%.2f", distance) + "公里" +", 坐標: [" + longitude + ", " + latitude + "]";}}/*** 示例用法*/public static void main(String[] args) {// 這里僅用于演示,實際使用應通過連接池獲取Jedis實例try (Jedis jedis = new Jedis("localhost", 6379)) {// 清除之前可能存在的測試數據jedis.del(USER_LOCATION_KEY);// 存儲幾個測試用戶的位置(廣州及周邊城市的坐標)saveUserLocation("user1", 113.267548, 23.142979, jedis);  // 廣州saveUserLocation("user2", 114.057868, 22.543099, jedis);  // 深圳saveUserLocation("user3", 113.030396, 22.938259, jedis);  // 佛山saveUserLocation("user4", 116.397128, 39.916527, jedis);  // 北京System.out.println("===== 查詢廣州周邊1000公里范圍內的用戶 =====");List<String> nearbyUsers = getNearbyUsers(113.267548, 23.142979, 1000, jedis);System.out.println("附近的用戶: " + nearbyUsers);System.out.println("\n===== 查詢廣州周邊1000公里范圍內的用戶(包含距離信息) =====");List<UserGeoInfo> nearbyUsersWithDist = getNearbyUsersWithDistance(113.267548, 23.142979, 1000, jedis);nearbyUsersWithDist.forEach(System.out::println);System.out.println("\n===== 計算用戶間距離 =====");double distance = getDistanceBetweenUsers("user1", "user2", jedis);System.out.println("廣州(user1)到深圳(user2)的距離: " + String.format("%.2f", distance) + "公里");distance = getDistanceBetweenUsers("user1", "user4", jedis);System.out.println("廣州(user1)到北京(user4)的距離: " + String.format("%.2f", distance) + "公里");System.out.println("\n===== 獲取用戶坐標 =====");double[] pos = getUserPosition("user1", jedis);if (pos != null) {System.out.println("用戶user1的坐標: [" + pos[0] + ", " + pos[1] + "]");}}}
}

使用Redis GEO的優勢與注意事項

優勢

  • 性能高效:Redis基于內存操作,地理位置查詢性能極高
  • 使用簡單:GEO命令集設計直觀,容易上手
  • 功能完善:提供了從添加、查詢到計算距離的完整功能集
  • 可擴展性好:可以輕松處理百萬級別的POI(興趣點)數據
  • 與Redis其他功能協同:可以結合Redis的緩存、事務等功能

注意事項

  • 精度限制:GEO命令的精度受到Geohash算法的限制,對于需要極高精度的應用場景(如軍事)可能不適用
  • 內存消耗:大量GEO數據會占用較多內存,需要合理規劃Redis服務器資源
  • 經緯度范圍:Redis GEO只接受有效的經緯度范圍(經度:-180到180,緯度:-85.05112878到85.05112878)
  • 數據持久化:使用AOF持久化模式可能會導致重啟時間延長,需權衡數據安全性和重啟速度
  • 適用場景:最適合"附近的XX"這類不需要復雜地理形狀計算的場景,如需多邊形區域計算等高級地理信息功能,可能需要專業GIS系統

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/899045.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/899045.shtml
英文地址,請注明出處:http://en.pswp.cn/news/899045.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

C語言貪吃蛇實現

When the night gets dark,remember that the Sun is also a star. 當夜幕降臨時&#xff0c;請記住太陽也是一顆星星。 ————《去月球海灘篇》 目錄 文章目錄 一、《貪吃蛇》游戲介紹 二、WIN32部分接口簡單介紹 2.1 控制臺窗口大小設置 2.2 命令行窗口的名稱的變更 2…

NIO入門

IO和NIO的區別&#xff1a; IO&#xff1a;通過流處理數據&#xff0c;僅支持阻塞IO。 核心組件&#xff1a;InputStream /OutputStream用于字節的讀寫&#xff0c;Reader / Writer&#xff1a;用于字符流的讀寫。讀取過程中無法被中斷&#xff0c;是阻塞式IO。 NIO:通過管道處…

基于vue.js開發的家庭裝修管理系統開發與設計(源碼+lw+部署文檔+講解),源碼可白嫖!

摘要 本家庭裝修管理系統采用B/S架構&#xff0c;數據庫是MySQL&#xff0c;網站的搭建與開發采用了先進的Node.js語言進行編寫&#xff0c;使用了VUE框架。該系統從兩個對象&#xff1a;由管理員和用戶來對系統進行設計構建。用戶的功能包括&#xff1a;注冊、登錄、瀏覽首頁…

OpenCV圖像拼接(5)圖像拼接模塊的用于創建權重圖函數createWeightMap()

操作系統&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 編程語言&#xff1a;C11 算法描述 cv::detail::createWeightMap 是 OpenCV 庫中用于圖像拼接模塊的一個函數&#xff0c;主要用于創建權重圖。這個權重圖在圖像拼接過程中扮演著重…

LangGraph 怎么控制遞歸次數

這一節我們講講langgraph圖的遞歸限制 Recursion Limit&#xff0c;遞歸限制設置了圖在單次執行過程中可以執行的最大超級步驟數。一旦達到該限制&#xff0c;LangGraph 將引發 GraphRecursionError 錯誤。默認情況下&#xff0c;此值設置為 25 步。遞歸限制可以在運行時為任何…

08-項目中不可控的任務如何安排和驗收

項目中有時會有一些任務的時間是不可控的&#xff0c;不可控的原因在于該工作完全受制于他人。意思就是如果其他人沒有做好&#xff0c;比如前后端同步開發&#xff0c;前端通常可能會快一些&#xff0c;然后要等后端提供接口&#xff0c;這個時候聯調工作是沒辦法開展的&#…

【Git】git cherry-pick(將某個分支的 commit 改動復制到當前分支)

文章目錄 ?一、基礎用法1.1、?應用單個提交1.2、?應用多個非連續提交1.3、?應用多個連續提交 ?二、高級用法?2.1、沖突處理?2.2、放棄操作?2.3、?不自動提交2.4、應用分支的最新提交 ?一、基礎用法 1.1、?應用單個提交 使用提交的哈希值&#xff08;可通過 git lo…

Milvus WeightedRanker 對比 RRF 重排機制

省流:優先選擇WeightedRanker 以rag為例,優先選擇bm25全文檢索,其次選擇向量檢索 Milvus混合搜索中的重排機制 Milvus通過hybrid_search() API啟用混合搜索功能&#xff0c;結合復雜的重排策略來優化多個AnnSearchRequest實例的搜索結果。本主題涵蓋了重排過程&#xff0c;…

C++手撕共享指針、多線程交替、LRU緩存

1. 共享指針 #include <atomic> #include <iostream>template <typename T> class sharedptr { private:T *ptr;std::atomic<size_t> *count;public:sharedptr(T *p) : ptr(p), count(new std::atomic<size_t>(1)) {}sharedptr(const sharedptr…

新版 eslintrc 文件棄用 .eslintignore已棄用 替代方案

1.進入eslint.config.mjs文件 2.import { defineConfig, globalIgnores } from "eslint/config"; 引入globalIgnores 3.配置 defineConfig([ ... globalIgnores([ "config/*", ".husky", ".local", "public/*", ".…

基于ngnix配置本地代理到對應服務器

遇到的問題&#xff1a;服務器可以訪問到的地址&#xff0c;我本地代碼無法訪問到 在服務器上裝好nginx&#xff0c;在nginx配置文件nginx.conf中配置本地想要鏈接對應的服務器端口信息 server {listen 8101;location / {proxy_http_version 1.1;proxy_set_header Upgrade $h…

Go 語言 fmt 模塊的完整方法詳解及示例

以下是 Go 語言 fmt 模塊的完整方法詳解及示例&#xff0c;涵蓋所有核心功能&#xff1a; 一、輸出函數 將數據寫入標準輸出、文件或字符串。 1. Print / Println / Printf 功能 Print: 寫入標準輸出&#xff0c;不換行。Println: 寫入標準輸出并換行。Printf: 格式化寫入標…

在K8S中使用ArgoCD做持續部署

一、了解argocd ArgoCD是一個基于Kubernetes的GitOps持續交付工具&#xff0c;應用的部署和更新都可以在Git倉庫上同步實現&#xff0c;并自帶一個可視化界面。本文介紹如何使用GitArgocd方式來實現在k8s中部署和更新應用服務。關于ci這一塊這里不多介紹。主要講解argocd如何實…

可視化圖解算法:刪除有序(排序)鏈表中重復的元素

1. 題目 描述 刪除給出鏈表中的重復元素&#xff08;鏈表中元素從小到大有序&#xff09;&#xff0c;使鏈表中的所有元素都只出現一次 例如&#xff1a; 給出的鏈表為1→1→2,返回1→2. 給出的鏈表為1→1→2→3→3返回1→2→3. 數據范圍&#xff1a;鏈表長度滿足 0≤n≤100…

ETL:數據清洗、規范化和聚合的重要性

在當今這個數據呈爆炸式增長的時代&#xff0c;數據已成為企業最為寶貴的資產之一。然而&#xff0c;數據的海量增長也伴隨著諸多問題&#xff0c;如數據來源多樣、結構復雜以及質量問題等&#xff0c;這些問題嚴重阻礙了數據的有效處理與深度分析。在此背景下&#xff0c;ETL&…

spring boot jwt生成token

1、引入jwt依賴 <!--jwt的依賴--> <dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>3.18.3</version> </dependency> 2、創建TokenUtils工具類 package com.pn.utils;import com.…

豪越科技:融合低空經濟的消防一體化安全管控解決方案

在科技飛速發展的當下&#xff0c;2025 年低空經濟正處于規模化商用的關鍵節點&#xff0c;其在應急救援、物流配送等多領域展現出巨大潛力。豪越科技憑借其先進的消防一體化安全管控方案&#xff0c;與低空經濟深度融合&#xff0c;打造出一套更為高效、智能的消防解決方案&am…

清華大學 | DeepSeek第十版!《DeepSeek政務應用場景及解決方案》

清華大學發布的《DeepSeek政務應用場景及解決方案》研究報告&#xff0c;聚焦人工智能技術DeepSeek在政府治理與公共服務中的創新實踐&#xff0c;系統闡述了其如何通過智能化技術賦能政務數字化轉型&#xff0c;推動治理能力現代化。報告指出&#xff0c;DeepSeek作為融合大數…

R語言雙Y軸折線圖+相關性注釋

目錄 包和數據 作圖代碼及詳解 包和數據 rm(list ls()) library(ggpubr) library(scales) library(ggplot2)data <- iris##鳶尾花數據集 data1 <- data[1:30,c(1,2,3)] > head(data1)Sepal.Length Sepal.Width Petal.Length 1 5.1 3.5 …

Android Compose 框架隱式動畫之過渡動畫深入剖析(二十六)

Android Compose 框架隱式動畫之過渡動畫深入剖析 一、引言 在移動應用開發領域&#xff0c;用戶體驗始終是至關重要的。動畫效果作為提升用戶體驗的關鍵元素&#xff0c;能夠為應用增添生動性和交互性。Android Compose 作為現代 Android UI 工具包&#xff0c;為開發者提供…