当前位置: 首页 > news >正文

Redis那些事儿(三)

文章目录

    • 1. 前言
    • 2. 常用api介绍
    • 3. 需求假设(获取离我最近的停车场)
    • 4. 代码示例

1. 前言

        接着上一篇Redis那些事儿(二) ,这一篇主要介绍Redis基于Geo数据结构实现的地理服务,它提供了一种方便的方式来存储和处理与地理位置相关的数据。Geo数据结构是Redis的一种特殊数据类型,用于存储地理位置信息,每个地理位置被表示为经度和纬度的坐标,可以将这些坐标与一个或多个成员关联起来。Redis的地理服务提供了一套简单而强大的功能,可以方便地存储和处理与地理位置相关的数据,它适用于许多应用场景,如地理定位、附近的人、附近的店铺搜索、附近的停车场、附近的地铁站…等等,大大提升了定位排序的效率。

2. 常用api介绍

        Redis地理服务API方法包括:GEOADD(向Geo数据结构中添加一个或多个地理位置信息);GEODIST(计算两个地理位置之间的距离);GEORADIUS(获取给定地理位置附近一定范围内的成员);GEOPOS(获取给定成员的经纬度坐标);GEOHASH(获取给定成员的Geohash值)…以上都是Geo地理服务内置的常用方法,接下来还是基于开发中的StringRedisTemplate对象作为切入点,更直观地说明实际应用中对于Geo地理服务地应用。
        StringRedisTemplate中定义了RedisGeoCommands的接口,RedisGeoCommands中封装了一系列的内置方法及子类,所以Redis中基于opsForGeo()的操作都离不开RedisGeoCommands,如下为部分截图:
RedisGeoCommands部分截图

Geo数据结构中存入坐标数据,redisTemplate.opsForGeo().add(key, locations)

		List<Park> parks = getParks(); //TODO 获取停车场列表信息//初始化Redis区域对象集合List<RedisGeoCommands.GeoLocation<String>> locations = new ArrayList<>();for (Park park : parks) {//实例化ponit对象,传参[经度、纬度]Point point = new Point(park.getLng(), park.getLat());//构造location对象,传参[name值(一般取ID)、point对象]RedisGeoCommands.GeoLocation<String> location = new RedisGeoCommands.GeoLocation<>(park.getParkId() + "", point);locations.add(location);}String key = "GEO_PARK_KEY";//存入坐标数据redisTemplate.opsForGeo().add(key, locations);

Geo数据结构中删除坐标数据,redisTemplate.opsForGeo().remove(key, …members)

		String key = "GEO_PARK_KEY";//删除单个坐标数据(parkId为单个停车场ID,类型为String)redisTemplate.opsForGeo().remove(key, parkId);//删除多个坐标数据(第一个参数为key,后面可以传入多个parkId)redisTemplate.opsForGeo().remove(key, parkId1, parkId2, parkId3);

Geo数据结构中检索坐标数据由近到远,redisTemplate.opsForGeo().radius(key, within, args)

		String key = "GEO_PARK_KEY";//检索20公里内的Distance distance = new Distance(20, Metrics.KILOMETERS);//以目标坐标为圆心,distance为半径的圆圈范围,其中lng代表中心坐标的经度、lat代表中心坐标的纬度Circle within = new Circle(new Point(lng, lat), distance);//条件参数,按照距离查询,默认就是升序RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance();//执行查询GeoResults<RedisGeoCommands.GeoLocation<String>> results = redisTemplate.opsForGeo().radius(key, within, args);

Geo数据结构中检索坐标数据由近到远,redisTemplate.opsForGeo().search(key, reference, distance, args)

		String key = "GEO_PARK_KEY";//检索20公里内的Distance distance = new Distance(20, Metrics.KILOMETERS);//条件参数,按照距离查询,默认就是升序RedisGeoCommands.GeoSearchCommandArgs args = RedisGeoCommands.GeoSearchCommandArgs.newGeoSearchArgs().includeDistance();//中心点位确认,其中lng代表中心坐标的经度、lat代表中心坐标的纬度GeoReference<String> reference = GeoReference.fromCoordinate(lng, lat);//执行查询GeoResults<RedisGeoCommands.GeoLocation<String>> results = redisTemplate.opsForGeo().search(key, reference, distance, args);

        以上列了四个最常用的方式,类似于我们最常规的CURD,其中最后两个radius和search是查询方法,二者最终查询的结果是一致的,只是手段方式不同而已!

3. 需求假设(获取离我最近的停车场)

        这个时候有人就说了:我使用GeodeticCalculator工具类在代码中计算距离也很方便的啊。我想了想,确实很方便,只需要引入geodesy的依赖,就可以直接使用GeodeticCalculator的calculateGeodeticCurve方法就可以计算了,还不需要麻烦的用redis搞那么长的代码了。但是,问题来了,就以停车场为例,假如只有十几个停车场,遍历一下然后按升序排个序,很快就计算出了离我最近的停车场列表了。如果我有上千个或者上万个停车场,总不能遍历上万次然后再排序吧,那这速度就一言难尽了…如果这个时候使用redis的geo数据结构来读取,那就完美解决这个问题了。Redis提供的GeoHash算法功能对于这方面的需求就太好用了,那么,附近的停车场、附近的人、附近的商家就都是一个思路了!

4. 代码示例

	/*** 获取距离最近的停车场列表,由近到远* @param lng 当前位置经度* @param lat 当前位置纬度* @param page 页数(第n页)* @param size 每页数量(10、20...)* @param value 公里范围内(搜索范围半径)* @return*/public List<Park> getLatestParks(Double lng, Double lat, Integer page, Integer size, Double value){//计算分页起始参数Integer start = (page - 1) * size;Integer end = page * size;//查询redis,按照距离排序String key = "GEO_PARK_KEY";//检索value公里内的Distance distance = new Distance(value, Metrics.KILOMETERS);//以目标坐标为圆心,distance为半径的圆圈范围,其中lng代表中心坐标的经度、lat代表中心坐标的纬度Circle within = new Circle(new Point(lng, lat), distance);//条件参数,按照距离查询,默认就是升序,截止到endRedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().limit(end);//执行查询GeoResults<RedisGeoCommands.GeoLocation<String>> results = redisTemplate.opsForGeo().radius(key, within, args);if (results == null) {return new ArrayList<>();}//获取最终检索的内容List<GeoResult<RedisGeoCommands.GeoLocation<String>>> content = results.getContent();//截取从起始到结束,如果总数小于起始数就证明已经页数超了,返回空集合if (content.size() <= start) {return new ArrayList<>();}//初始化parkId集合List<Long> parkIds = new ArrayList<>();//初始化距离mapMap<String, Distance> distanceMap = new HashMap<>();//分页跳过之前的数据,并遍历赋值content.stream().skip(start).forEach(i->{String parkIdStr = i.getContent().getName();parkIds.add(Long.valueOf(parkIdStr));Distance dis = i.getDistance();distanceMap.put(parkIdStr, dis);});//固定排序String join = StringUtils.join(parkIds,",");//根据parkId集合获取park集合List<Park> newParks = parkService.list(new QueryWrapper<Park>().in("park_id", parkIds).last("ORDER BY FIELD(park_id," + join + ")")).stream().map(i -> {i.setDistance(distanceMap.get(i.getParkId() + "").getValue());return i;}).collect(Collectors.toList());return newParks;}

以上代码为由近到远获取距离最近的停车场列表的示例方法,仅供参考

相关文章:

  • Minio多节点多驱动分布式部署官网文档翻译
  • C#将字符串(string)转换为整数(int)几种常见的方法
  • Leetcode—100.相同的树【简单】
  • Linux--线程-条件控制实现线程的同步
  • 在Google Kubernetes集群创建分布式Jenkins(一)
  • vue中app.use()做了什么
  • CSRF攻击(2), 绕过Referer防御
  • 英语——分享篇——每日200词——201-400
  • 基于单片机的智能饮水机系统
  • Luancher和unityLibrary都有build.gradle有什么不同
  • 合肥中科深谷嵌入式项目实战——人工智能与机械臂(六)
  • Java中的异常处理机制是怎样的?
  • golang实现极简todolist
  • 二进制搭建 Kubernetes v1.20
  • 【LeetCode力扣】287.寻找重复数
  • __proto__ 和 prototype的关系
  • 【腾讯Bugly干货分享】从0到1打造直播 App
  • 77. Combinations
  • codis proxy处理流程
  • Github访问慢解决办法
  • macOS 中 shell 创建文件夹及文件并 VS Code 打开
  • NSTimer学习笔记
  • Sublime text 3 3103 注册码
  • 不用申请服务号就可以开发微信支付/支付宝/QQ钱包支付!附:直接可用的代码+demo...
  • 给github项目添加CI badge
  • 解析带emoji和链接的聊天系统消息
  • 开发基于以太坊智能合约的DApp
  • 浅谈Golang中select的用法
  • 浅谈JavaScript的面向对象和它的封装、继承、多态
  • 使用putty远程连接linux
  • 首页查询功能的一次实现过程
  • 思否第一天
  • 延迟脚本的方式
  • 一起来学SpringBoot | 第十篇:使用Spring Cache集成Redis
  • 原生js练习题---第五课
  • ​决定德拉瓦州地区版图的关键历史事件
  • #Java第九次作业--输入输出流和文件操作
  • $ is not function   和JQUERY 命名 冲突的解说 Jquer问题 (
  • $.extend({},旧的,新的);合并对象,后面的覆盖前面的
  • (11)工业界推荐系统-小红书推荐场景及内部实践【粗排三塔模型】
  • (C语言)共用体union的用法举例
  • (办公)springboot配置aop处理请求.
  • (二)Eureka服务搭建,服务注册,服务发现
  • (一)RocketMQ初步认识
  • ****三次握手和四次挥手
  • ..thread“main“ com.fasterxml.jackson.databind.JsonMappingException: Jackson version is too old 2.3.1
  • .bat批处理(十):从路径字符串中截取盘符、文件名、后缀名等信息
  • .NET Core WebAPI中使用swagger版本控制,添加注释
  • .net 调用海康SDK以及常见的坑解释
  • .NET 服务 ServiceController
  • .NET 实现 NTFS 文件系统的硬链接 mklink /J(Junction)
  • .NET 自定义中间件 判断是否存在 AllowAnonymousAttribute 特性 来判断是否需要身份验证
  • .net流程开发平台的一些难点(1)
  • .net中生成excel后调整宽度
  • :“Failed to access IIS metabase”解决方法