Redis那些事儿(三)

news2024/11/28 21:35:07

文章目录

    • 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);
        //条件参数,按照距离查询,默认就是升序,截止到end
        RedisGeoCommands.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<>();
        //初始化距离map
        Map<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;
    }

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1174827.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

linux 创建git项目并提交到gitee(保姆式教程)

01、git安装与初始化设置 mhzzjmhzzj-virtual-machine:~/work/skynetStudy$ apt install mhzzjmhzzj-virtual-machine:~/work/skynetStudy$ git config --global user.name "用户名" mhzzjmhzzj-virtual-machine:~/work/skynetStudy$ git config --global user.ema…

Instant-NGP论文笔记

文章目录 论文笔记 论文笔记 instant-ngp的nerf模型与vanilla nerf的模型架构相同。 instant-ngp的nerf模型包含两个MLP&#xff0c;第一个MLP就两个全连接&#xff0c;输入维度是32&#xff08;16层分辨率x2&#xff09;&#xff0c;输出是16&#xff08;用于预测密度&#x…

SpringBoot配置文件优先级

1.idea临时属性 说明&#xff1a;Program arguments配置--server.port8082 --ab&#xff1b;意思是将端口改成了8082。这个优先级最高。 2.resource 说明&#xff1a;创建config文件里面的yml文件。 3.jar包同级&#xff08;yml&#xff09; 说明&#xff1a;创建一个yml文件…

机器学习中的关键组件

机器学习中的关键组件 数据 每个数据集由一个个样本组成&#xff0c;大多时候&#xff0c;它们遵循独立同分布。样本有时也叫作数据点或数据实例&#xff0c;通常每个样本由一组称为特征或协变量的属性组成。机器学习会根据这些属性进行预测&#xff0c;预测得到的称为标签或…

平面扫描(Plane-sweeping)深度体会

先看文章 三维重建之平面扫描算法&#xff08;Plane-sweeping&#xff09;_plane sweeping_小玄玄的博客-CSDN博客 Plane Sweeping | 平面扫描 - 知乎 (zhihu.com) 注意平面Dm,这是其中一个平面&#xff0c;平面上有一个M点&#xff0c;这个点也再物体上。所以会被摄像机看到…

Idea去掉显示的测试覆盖率

一.启东时 误点击了 快捷键调出 【Ctrl 】【Alt】【F6】

优雅的 Dockerfile 是怎样炼成的?

Docker 简介 目前&#xff0c;Docker 主要有两个形态&#xff1a;Docker Desktop 和 Docker Engine。 Docker Desktop 是专门针对个人使用而设计的&#xff0c;支持 Mac&#xff08;已支持arm架构的M系芯片&#xff09; 和 Windows 快速安装&#xff0c;具有直观的图形界面&a…

数据结构—字符串

文章目录 7.字符串(1).字符串及其ADT#1.基本概念#2.ADT (2).字符串的基本操作#1.求子串substr#2.插入字符串insert#3.其他操作 (3).字符串的模式匹配#1.简单匹配(Brute-Force方法)#2.KMP算法I.kmp_match()II.getNext() #3.还有更多 小结附录&#xff1a;我自己写的string 7.字符…

手搭手Ajax实现搜索地址自动补全功能

输入单词后&#xff0c;自动提示出要搜索的信息&#xff0c;点击某个内容后&#xff0c;自动补全至搜索框。 比如&#xff1a; 如何实现搜索自动补全功能 键盘事件&#xff1a;keyup按键弹起事件发送ajax请求&#xff0c;请求中提交用户输入的搜索内容,后端接收内容后&#x…

23种设计模式-Java语言实现

因为要准备一个考试所以又重新接触到了设计模式&#xff0c;之前只是别人说什么就是什么&#xff0c;记下就好了&#xff0c;完全不理解其中的思想以及为什么要用(虽然现在也不太理解…) 先慢慢总结吧&#xff0c;常读常新。 23种设计模式 “每一个模式描述了一个在我们周围不…

C++进阶篇4---set和map

一、关联式容器 在初阶篇中&#xff0c;我们已经接触过STL中的部分容器&#xff0c;比如&#xff1a;vector、list、deque等&#xff0c;这些容器统称为序列式容器&#xff0c;因为其底层为线性序列的数据结构&#xff0c;里面存储的是元素本身。 那什么是关联式容器&#xff1…

【unity实战】Unity实现2D人物双击疾跑

最终效果 前言 我们要实现的功能是双击疾跑&#xff0c;当玩家快速地按下同一个移动键两次时能进入跑步状态 我假设快速按下的定义为0.2秒内&#xff0c;按下同一按键两次 简单的分析一下需求&#xff0c;实现它的关键在于获得按键按下的时间&#xff0c;我们需要知道第一次…

eBPF BCC开源工具简介

目录 官方链接 编译安装 ubuntu版本 安装 examples tools hello_world.py demo 运行报错 网上目前的解决办法 错误分析过程 python版本检测 libbcc库检查 python3 bcc库检查 正常输出 监控进程切换 运行输出 监控CPU直方图 缓存命中率监控&#xff1a;caches…

英语——分享篇——每日200词——201-400

201——feel——[fi:l]——vt.摸&#xff0c;感觉&#xff0c;认为&#xff1b;n.感觉&#xff0c;触摸——feel——f斧头(编码)ee眼睛(象形)l棍子(编码)——斧头用眼看&#xff0c;棍子用手摸——The metal felt smooth and cold.——这种金属摸起来冰冷而光滑。 202——cleve…

SpringBoot项目打包与运行

1.clean生命周期 说明&#xff1a;为了项目能够正确打包&#xff0c;先清理打包文件。 2.package生命周期 说明&#xff1a;打包后生成以下目录。 2.1问题 说明&#xff1a;springboot_08_ssmp-0.0.1-SNAPSHOT.jar中没有主清单属性。 2.2解决 说明&#xff1a;注释skip&…

[LeetCode]-160. 相交链表-141. 环形链表-142.环形链表II-138.随机链表的复制

目录 160.相交链表 题目 思路 代码 141.环形链表 题目 思路 代码 142.环形链表II 题目 思路 代码 160.相交链表 160. 相交链表 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/intersection-of-two-linked-lists/description/ 题目 给你两个…

Node问题:如何正确安装nvm?Mac和Win双教程!

前端功能问题系列文章&#xff0c;点击上方合集↑ 序言 大家好&#xff0c;我是大澈&#xff01; 本文约1700字&#xff0c;整篇阅读大约需要3分钟。 本文主要内容分三部分&#xff0c;第一部分是需求分析&#xff0c;第二部分是实现步骤&#xff0c;第三部分是问题详解。 …

为什么有了MAC地址,还需要IP地址?

解释 搞懂这个问题&#xff0c;首先需要了解交换机的功能 交换机内部有一张MAC地址映射表&#xff0c;记录着MAC地址和端口的对应关系。 如果A要给B发送一个数据包&#xff0c;构造如下格式的数据结构&#xff1a; 到达交换机时&#xff0c;交换机内部通过自己维护的 MAC 地…

ConcurrentHashMap是如何实现线程安全的

目录 原理&#xff1a; 初始化数据结构时的线程安全 put 操作时的线程安全 原理&#xff1a; 多段锁cassynchronize 初始化数据结构时的线程安全 在 JDK 1.8 中&#xff0c;初始化 ConcurrentHashMap 的时候这个 Node[] 数组是还未初始化的&#xff0c;会等到第一次 put() 方…

常见面试题-MySQL专栏(三)MVCC、BufferPool

typora-copy-images-to: imgs 了解 MVCC 吗&#xff1f; 答&#xff1a; MVCC&#xff08;Multi-Version Concurrency Control&#xff09; 是用来保证 MySQL 的事务隔离性的&#xff0c;对一行数据的读和写两个操作默认是不会通过加锁互斥来保证隔离性&#xff0c;避免了频…