Redis --- 使用GEO实现经纬度距离计算
什么是GEO?

Spring Boot 项目中可以通过 Spring Data Redis 来使用 Redis GEO 功能,主要通过 RedisTemplate 和 GeoOperations 接口来操作地理位置数据。
@Service
public class GeoService {@Autowiredprivate RedisTemplate<String, Object> redisTemplate;// 存储地理位置public void addGeoLocation(String key, String member, double longitude, double latitude) {GeoOperations<String, Object> geoOperations = redisTemplate.opsForGeo();geoOperations.add(key, new GeoLocation<>(member, longitude, latitude));}// 获取两个地理位置之间的距离public Double getDistance(String key, String member1, String member2) {GeoOperations<String, Object> geoOperations = redisTemplate.opsForGeo();return geoOperations.distance(key, member1, member2, GeoUnit.KILOMETERS).getValue();}// 查找指定经纬度范围内的地理位置public List<GeoLocation<Object>> getNearbyLocations(String key, double longitude, double latitude, double radius) {GeoOperations<String, Object> geoOperations = redisTemplate.opsForGeo();return geoOperations.radius(key, longitude, latitude, radius, GeoUnit.KILOMETERS);}
}
示例调用:
GeoService geoService = new GeoService();// 添加地理位置
geoService.addGeoLocation("cities", "Beijing", 116.4074, 39.9042);
geoService.addGeoLocation("cities", "Shanghai", 121.4737, 31.2304);// 计算距离
Double distance = geoService.getDistance("cities", "Beijing", "Shanghai");
System.out.println("Distance between Beijing and Shanghai: " + distance + " km");// 查找附近的地点
List<GeoLocation<Object>> nearbyCities = geoService.getNearbyLocations("cities", 116.4074, 39.9042, 100);
for (GeoLocation<Object> city : nearbyCities) {System.out.println(city.getName());
}
业务需求:
对附近的商户进行搜索
首先导入redis依赖:
<dependency><groupId>org.springframework.data</groupId><artifactId>spring-data-redis</artifactId><version>2.7.18</version>
</dependency>
<dependency><groupId>io.lettuce</groupId><artifactId>lettuce-core</artifactId><version>6.4.1.RELEASE</version>
</dependency>
随后按照商户类型做分组,类型相同的商户作为一组,以typeId为key存入同一个GEO集合中:

@SpringBootTest
public class HmDianPingApplicationTests {@Autowiredprivate IShopService shopService;@Resourceprivate StringRedisTemplate stringRedisTemplate;@Testpublic void loadShopData(){// 查询店铺信息List<Shop> list = shopService.list();// 把店铺按照typeId分组,typeId一致的放入同一集合中Map<Long,List<Shop>> map = list.stream().collect(Collectors.groupingBy(shop -> shop.getTypeId()));for(Map.Entry<Long,List<Shop>> entry:map.entrySet()){Long typeId = entry.getKey();String key = "shop:geo" + typeId;List<Shop> value = entry.getValue();// GeoLocation的泛型是member的数据类型// GeoLocation的内部是member与point,point是经纬度List<RedisGeoCommands.GeoLocation<String>> locations = new ArrayList<>();// 写入Redis GEOADD key 经度 纬度 memberfor(Shop shop:value){// 效率低(有多少个店铺就需要存入多少次)//stringRedisTemplate.opsForGeo().add(key, new Point(shop.getX(),shop.getY()), shop.getId().toString());// 使用可迭代的集合直接存入locations.add(new RedisGeoCommands.GeoLocation<>(shop.getId().toString(),new Point(shop.getX(), shop.getY())));}stringRedisTemplate.opsForGeo().add(key, locations);}}
}

根据上面的接口文档,在shopController中写入接口:
@RestController
@RequestMapping("/shop")
public class ShopController {@Resourcepublic IShopService shopService;/*** 根据商铺类型分页查询商铺信息* @param typeId 商铺类型* @param current 页码* @return 商铺列表*/@GetMapping("/of/type")public Result queryShopByType(@RequestParam("typeId") Integer typeId,@RequestParam(value = "current", defaultValue = "1") Integer current,@RequestParam(value = "x",required = false) Double x, // 可为空@RequestParam(value = "y",required = false) Double y // 可为空) {return shopService.queryShopByType(typeId,current,x,y);}
}
下面通过 Redis 和数据库的结合实现了商店查询功能。使用 Redis 的地理位置功能来加速根据坐标查询商店的速度并支持分页。首先检查是否提供了坐标,如果没有坐标就直接从数据库查询,如果有坐标则通过 Redis 查询地理位置数据并计算距离,最终返回分页后的商店信息。
@Service // 声明为 Spring 服务类,允许 Spring 自动管理该类的生命周期
public class ShopServiceImpl extends ServiceImpl<ShopMapper, Shop> implements IShopService {// 引入 Redis 的 StringRedisTemplate,用于执行 Redis 操作@Resourceprivate StringRedisTemplate stringRedisTemplate;@Overridepublic Result queryShopByType(Integer typeId, Integer current, Double x, Double y) {// 如果没有提供坐标 x 和 y,则不需要通过 Redis 进行地理查询,直接通过数据库查询商店if(x == null || y == null){// 构建数据库查询条件:根据商店类型查询,并分页Page<Shop> page = query().eq("type_id", typeId) // 根据商店类型 ID 进行筛选.page(new Page<>(current, 1000)); // 分页查询,每页返回 1000 条记录return Result.ok(page.getRecords()); // 返回查询结果}// 如果提供了坐标,计算分页参数int from = (current - 1) * 1000; // 计算当前页起始记录int end = current * 1000; // 计算当前页结束记录// 查询 Redis,按照距离排序进行分页,结果包括 shopId 和距离String key = "shop:geo:" + typeId; // Redis 中存储商店位置的 key,格式为 shop:geo:typeIdGeoResults<RedisGeoCommands.GeoLocation<String>> results = stringRedisTemplate.opsForGeo().search(key, // Redis 中的 Geo 数据的 keyGeoReference.fromCoordinate(x, y), // 从提供的坐标 (x, y) 开始查询new Distance(5000), // 设置搜索半径为 5000 米RedisGeoCommands.GeoSearchCommandArgs.newGeoSearchArgs().includeDistance().limit(end) // 包括距离信息,并限制查询结果数量);// 如果 Redis 查询结果为空,则直接返回空列表if(results == null) {return Result.ok(Collections.emptyList());}// 获取查询结果的内容List<GeoResult<RedisGeoCommands.GeoLocation<String>>> list = results.getContent();// 如果查询结果中没有足够的记录显示当前页的内容,返回空列表if(list.size() <= from){return Result.ok(Collections.emptyList());}// 使用 stream 流式处理查询结果,跳过前 from 条记录,获取分页结果List<Long> ids = new ArrayList<>(list.size()); // 存储查询结果中商店的 IDMap<String, Distance> distanceMap = new HashMap<>(list.size()); // 存储商店 ID 与其对应的距离list.stream().skip(from).forEach(result -> {// 获取店铺的 ID,GeoLocation.getName() 方法返回的是商店 IDString shopIdStr = result.getContent().getName();ids.add(Long.parseLong(shopIdStr)); // 将商店 ID 转换为 Long 类型并存入 ids 列表// 获取商店距离Distance distance = result.getDistance();distanceMap.put(shopIdStr, distance); // 将商店 ID 和距离存入 distanceMap});// 构建 SQL 查询,查询所有符合条件的商店String idStr = StrUtil.join(",", ids); // 将 ids 列表转化为逗号分隔的字符串List<Shop> shops = query().in("id", ids) // 查询商店 ID 在 ids 列表中的所有商店.last("order by ids " + idStr + ")") // 通过 SQL 排序,根据商店 ID 排序.list(); // 执行查询并返回商店列表// 遍历商店信息,将每个商店的距离装入到 Shop 对象中for (Shop shop : shops) {shop.setDistance(distanceMap.get(shop.getId().toString()).getValue()); // 设置商店的距离}// 返回查询结果return Result.ok(shops); // 返回商店信息}
}
通过将 Redis 的地理位置数据与数据库中的商店信息结合,优化了查询的效率,尤其适合商店类型和位置相关的查询场景。
相关文章:
Redis --- 使用GEO实现经纬度距离计算
什么是GEO? Spring Boot 项目中可以通过 Spring Data Redis 来使用 Redis GEO 功能,主要通过 RedisTemplate 和 GeoOperations 接口来操作地理位置数据。 Service public class GeoService {Autowiredprivate RedisTemplate<String, Object> red…...
【0403】Postgres内核 检查(procArray )给定 db 是否有其他 backend process 正在运行
文章目录 1. 给定 db 是否有其他 backend 正在运行1.1 获取 allPgXact[] 索引1.1.1 MyProc 中 databaseId 初始化实现1.2 allProcs[] 中各 databaseId 判断1. 给定 db 是否有其他 backend 正在运行 CREATE DATABASE 语句创建用户指定 数据库名(database-name)时候, 会通过 …...
[数据结构] Set的使用与注意事项
目录 Set的说明 常见方法说明 注意事项 TreeSet使用案例 Set的说明 Set与Map主要的不同有两点: Set是继承自Collection的接口类,Set中只存储了Key. 常见方法说明 方法解释boolean add(E e)添加元素,但重复元素不会被添加成功void clear()清空集合boolean contains(Object…...
amis组件crud使用踩坑
crud注意 过滤条件参数同步地址栏 默认 CRUD 会将过滤条件参数同步至浏览器地址栏中,比如搜索条件、当前页数,这也做的目的是刷新页面的时候还能进入之前的分页。 但也会导致地址栏中的参数数据合并到顶层的数据链中,例如:自动…...
离线统信系统的python第三方库批量安装流程
一、关于UOS本机 操作系统:UOS(基于Debian的Linux发行版) CPU:海光x86 二、具体步骤 1、在联网的电脑上用控制台的pip命令批量下载指定版本的第三方库 方法A cd <目标位置的绝对路径> pip download -d . --platform many…...
韶音科技:消费电子行业售后服务实现数字化转型,重塑客户服务体系
韶音科技:消费电子行业售后服务实现数字化转型,重塑客户服务体系 在当今这个科技日新月异的时代,企业之间的竞争早已超越了单纯的产品质量比拼,**售后服务成为了衡量消费电子行业各品牌实力与客户满意度的关键一环。**深圳市韶音…...
神经网络|(九)概率论基础知识-泊松分布及python仿真
【1】引言 在前序学习进程中,我们已经知晓二项分布是多重伯努利分布,二伯努利分布对应的是可以无限重复、结果只有两种可能的随机试验。 相关文章链接为: 神经网络|(八)概率论基础知识-二项分布及python仿真-CSDN博客 上述文章还调用nump…...
114,【6】攻防世界 web wzsc_文件上传
进入靶场 传个桌面有的 直接空白了 我们 访问一下上传的东西 /index 没显示用于解析的.htaccess和.user.ini 文件,还两个都不显示 .htaccess 和 .user.ini 文件分别用于 Apache 服务器和 PHP-FPM 环境的目录级配置 但上传的时候bp查看状态码是200,…...
【Kubernetes的SpringCloud最佳实践】有Service是否还需要Eureka?
在 Kubernetes 中部署 Spring Cloud 微服务时,是否还需要 Eureka 取决于具体场景和架构设计。以下是详细的实践建议和结论: 1. Kubernetes 原生服务发现 vs Eureka Kubernetes 自身提供了完善的服务发现机制(通过 Service 资源)&…...
SQL最佳实践(笔记)
写在前面: 之前baeldung的Java Weekly Reviews里面推荐了一篇关于SQL优化的文章,正好最近在学习数据库相关知识,记一些学习笔记 原文地址:SQL Best Practices Every Java Engineer Must Know 1. 使用索引 使用索引…...
vue3学习四
七 标签ref属性 设置标签ref属性,类似于设置标签id。 普通标签 <template name"test4"> <p ref"title" id"title" click"showinfo">VIEW4</p> <View3/><script lang"ts" setup>…...
C# LiteDB 使用教程
一、引言 在软件开发中,数据存储和管理是至关重要的一环。对于小型项目或者对性能和便捷性有较高要求的场景,传统的大型数据库可能显得过于笨重。而 LiteDB 作为一款轻量级的嵌入式 NoSQL 数据库,为开发者提供了一个简洁、高效的解决方案。它…...
Python Pandas(3):DataFrame
1 介绍 DataFrame 是 Pandas 中的另一个核心数据结构,类似于一个二维的表格或数据库中的数据表。它含有一组有序的列,每列可以是不同的值类型(数值、字符串、布尔型值)。DataFrame 既有行索引也有列索引,它可以被看做由…...
使用通义灵码 ai编程 来提高开发效率
1、我们先新建一个Hello,world的vue3项目(快速上手 | Vue.js) 创建好以后,运行以下界面: about界面如下,现在我们让灵码给我们修改一下这个字体的颜色及加点其它的样式: 2、先选中样式…...
【OpenCV】入门教学
🏠大家好,我是Yui_💬 🍑如果文章知识点有错误的地方,请指正!和大家一起学习,一起进步👀 🚀如有不懂,可以随时向我提问,我会全力讲解~ ὒ…...
大数据项目4:基于spark的智慧交通项目设计与实现
项目概述 项目直达 www.baiyuntu.com 随着交通数据的快速增长,传统的交通管理方式已无法满足现代城市的需求。交通大数据分析系统通过整合各类交通数据,利用大数据技术解决交通瓶颈问题,提升交通管理效率。本项目旨在通过大数据技术&#…...
netcore openTelemetry+prometheus+grafana
一、netcore项目 二、openTelemetry 三、prometheus 四、grafana添加Dashborad aspire/src/Grafana/dashboards at main dotnet/aspire GitHub 导入:aspnetcore.json和aspnetcore-endpoint.json 效果:...
Spring Boot接入Deep Seek的API
1,首先进入deepseek的官网:DeepSeek | 深度求索,单击右上角的API开放平台。 2,单击API keys,创建一个API,创建完成务必复制!!不然关掉之后会看不看api key!!&…...
Git、Github和Gitee完整讲解:丛基础到进阶功能
第一部分:Git 是什么? 比喻:Git就像是一本“时光机日记本” 每一段代码的改动,Git都会帮你记录下来,像是在写日记。如果出现问题或者想查看之前的版本,Git可以带你“穿越回过去”,找到任意时间…...
MyBatis的工作流程是怎样的?
大家好,我是锋哥。今天分享关于【MyBatis的工作流程是怎样的?】面试题。希望对大家有帮助; MyBatis的工作流程是怎样的? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 MyBatis 的工作流程可以分为几个主要的步骤&…...
利用最小二乘法找圆心和半径
#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...
linux之kylin系统nginx的安装
一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源(HTML/CSS/图片等),响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址,提高安全性 3.负载均衡服务器 支持多种策略分发流量…...
调用支付宝接口响应40004 SYSTEM_ERROR问题排查
在对接支付宝API的时候,遇到了一些问题,记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…...
golang循环变量捕获问题
在 Go 语言中,当在循环中启动协程(goroutine)时,如果在协程闭包中直接引用循环变量,可能会遇到一个常见的陷阱 - 循环变量捕获问题。让我详细解释一下: 问题背景 看这个代码片段: fo…...
以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:
一、属性动画概述NETX 作用:实现组件通用属性的渐变过渡效果,提升用户体验。支持属性:width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项: 布局类属性(如宽高)变化时&#…...
边缘计算医疗风险自查APP开发方案
核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...
visual studio 2022更改主题为深色
visual studio 2022更改主题为深色 点击visual studio 上方的 工具-> 选项 在选项窗口中,选择 环境 -> 常规 ,将其中的颜色主题改成深色 点击确定,更改完成...
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...
leetcodeSQL解题:3564. 季节性销售分析
leetcodeSQL解题:3564. 季节性销售分析 题目: 表:sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...
Docker 本地安装 mysql 数据库
Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker ;并安装。 基础操作不再赘述。 打开 macOS 终端,开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...
