Redis 篇-深入了解使用 Redis 中的 GEO 数据结构实现查询附近店铺、BitMap 实现签到功能、HyperLogLog 实现 UV 流量统计

🔥博客主页: 【小扳_-CSDN博客】
❤感谢大家点赞👍收藏⭐评论✍

文章目录

        1.0 GEO 数据结构的基本用法

        1.1 使用 GEO 导入数据

        1.2 使用 GEO 实现查找附近店铺功能

        2.0 BitMap 基本用法

        2.1 使用 BitMap 实现签到功能

        2.2 统计连续签到功能

        3.0 HyperLogLog 基本用法

        3.1 测试使用 HyperLogLog 统计数据


        1.0 GEO 数据结构的基本用法

        GEO 就是 Geolocation 的简写形式,代表地理坐标。Redis 在 3.2 版本中加入了对 GEO 的支持,允许存储地理坐标信息,帮助我们根据经纬度来检索数据。常见的命名有:

        1)GEOADD:添加一个地理空间信息,包含:经度(longitude)、纬度(latitude)、值(member)。

GEOADD key [NX|XX] [CH] longitude latitude member [longitude latitude member ...]

        2)GEODIST:计算指定的两个点之间的距离并返回。

GEODIST key member1 member2 [unit]

        需要注意的是:距离默认是以 m 为单位。

        3)GEOHASH:将指定的 member 的坐标转为 hash 字符串形式并返回。

GEOHASH key member [member ...]

        4)GEOPOS:返回指定 member 的坐标。

GEOPOS key member [member ...]

        5)GEOSEARCH:在指定范围内搜索 member,并按照与指定点之间的距离排序后返回。范围可以是圆形或矩形。

GEOSEARCH key longitude latitude radius [WITHDIST]

        6)GEOSEARCHSTORE:与 GEOSEARCH 功能一致,不过可以把结果存储到一个指定的 key 。

GEOSEARCHSTORE destination key longitude latitude radius [WITHDIST] [WITHCOORD] [WITHHASH] [SORTBY field [ASC|DESC]] [COUNT count]

 具体参数:

        destination: 结果将被存储的目标键。

        key: 包含地理位置数据的键。

        longitude: 搜索中心的经度。

        latitude: 搜索中心的纬度。

        radius: 搜索半径,可以是距离单位(如 km 或 m)。

        WITHDIST: 可选参数,返回每个结果与中心点的距离。

        WITHCOORD: 可选参数,返回每个结果的经纬度坐标。

        WITHHASH: 可选参数,返回每个结果的地理哈希值。

        SORTBY field: 可选参数,按指定字段排序(如距离)。

        COUNT count: 可选参数,限制返回的结果数量。

        1.1 使用 GEO 导入数据

        一般来说,用户在查询信息的时候,通过分类来进行的查询用户想要的结果,比如说:用户根据美食、KTV、酒店等等不同的店铺分类来进行选择。

        因此,通过设置 key 为分类 ID 将店铺分类。

        GEO 底层的数据结构是 SortedSet 实现的,将 Score 设置为通过经纬度算出来的数据,而 value 设置为 member ,该值一般是表示店铺的信息,也就是可以将其设置为店铺 ID 。

具体思路:

        将存放在数据库中的店铺信息缓存到 Redis 中:

        将数据库中的店铺信息全部查询出来,解决根据店铺的分类,将店铺进行分类,使用 map 来接收:key 为 typeId,value 为 shop 店铺的具体信息。最后就可以将数据存放到缓存中。

代码如下:

    @AutowiredStringRedisTemplate stringRedisTemplate;@AutowiredShopMapper shopMapper;@Testvoid text1(){//先获取店铺信息List<Shop> list = shopMapper.getList();//将店铺进行分类Map<Integer, List<Shop>> map = list.stream().collect(Collectors.groupingBy(Shop::getTypeId));//接着将信息放入到Redis中for (Map.Entry<Integer, List<Shop>> entry : map.entrySet()) {//店铺IDInteger id = entry.getKey();String key = "shop:"+id;//当前类的店铺List<Shop> value = entry.getValue();List<RedisGeoCommands.GeoLocation<String>> locations = new ArrayList<>(value.size());for (Shop shop : value) {locations.add(new RedisGeoCommands.GeoLocation<>(shop.getId().toString(), new Point(shop.getX(), shop.getY())));}stringRedisTemplate.opsForGeo().add(key,locations);}}

运行结果:

        1.2 使用 GEO 实现查找附近店铺功能

        需求:根据指定的经纬度、页码、店铺类型参数来进行查找店铺信息。

        具体思路:

        对于店铺类型可以根据店铺分类 ID 来进行过滤,当前页码 current 可以求出 from 起始开始的地方和 end 结尾的地方:from = (current - 1) * size ,end = current * size ,其中 size 是指定每页的最大数量。

代码如下:

    @Testvoid text2(){List<Shop> shops = getShops(2, 120.149192, 30.316078,1);System.out.println(shops);}private List<Shop> getShops(int current, double x, double y,int typeId){//根据分类ID来查找String key = "shop:"+ typeId;//起始地址int from = (current - 1) * 2;//最终地址int end = current * 2;//使用范围查找5公里以内的店铺,且进行分页查询GeoResults<RedisGeoCommands.GeoLocation<String>> results =stringRedisTemplate.opsForGeo().search(key,GeoReference.fromCoordinate(new Point(x, y)),new Distance(5000),RedisGeoCommands.GeoSearchCommandArgs.newGeoSearchArgs().includeDistance().limit(end));if (results == null){return null;}//获取符合条件的店铺列表List<GeoResult<RedisGeoCommands.GeoLocation<String>>> content = results.getContent();if (content.size() <= from){return null;}//跳过前 from 个店铺,这就可以获取到 from - end 个店铺的信息List<GeoResult<RedisGeoCommands.GeoLocation<String>>> collect = content.stream().skip(from).toList();List<Integer> id = new ArrayList<>(collect.size());Map<Integer,Double> shopMap = new HashMap<>(collect.size());for (GeoResult<RedisGeoCommands.GeoLocation<String>> result : collect) {//拿到了店铺IDid.add(Integer.valueOf(result.getContent().getName()));//再拿到店铺距离Distance distance = result.getDistance();shopMap.put(Integer.valueOf(result.getContent().getName()), distance.getValue());}//根据店铺ID来查询具体的店铺信息List<Shop> list = shopMapper.getShopList(id);for (Shop shop : list) {shop.setDistance(shopMap.get(shop.getId()));}return list;}

        2.0 BitMap 基本用法

常见的命令:

        1)SETBIT:向指定位置(offset)存入一个 0 或者 1。

SETBIT key offset value

        2)GETBIT:获取指定位置(offset)的 bit 值。

GETBIT key offset

        3)BITCOUNT:统计 BitMap 中值为 1 的 bit 位的数量。

BITCOUNT key [start] [end]

        4)BITFIELD:操作(查询、修改、自增)BitMap 中 bit 数组中指定位置(offset)的值。

BITFIELD key [GET type offset] [SET type offset value] [INCRBY type offset increment] [OVERFLOW overflow]

         offset:从第几个开始

        type:u1 无符号查询,查询 1 个、i3 有符号查询,查询 3 个。

        5)BITOP:将多个 BitMap 的结果做位运算。

BITOP operation destkey key [key ...]

        6)BITPOS:查找 bit 数组中指定范围内第一个 0 或者 1 出现的位置。

        

BITPOS key bit [start] [end]

        2.1 使用 BitMap 实现签到功能

        把每一个 bit 对应当月的每一天,形成了映射关系。用 0 和 1 表示业务状态,这种思路就称为位图。 Redis 中是利用 string 类型数据结构实现 BitMap ,因此最大上限是 512 M,转换为 bit 则是 2^32 个 bit 位。

代码实现:

    @Testvoid text3(){//用户1sign(1);//用户2sign(2);//用户3sign(3);}private void sign(Integer userId){//获取当前时间LocalDateTime now = LocalDateTime.now();String date = now.format(DateTimeFormatter.ofPattern("yyyyMM:"));String key = "sign:"+ date +userId;//当前天数stringRedisTemplate.opsForValue().setBit(key,now.getDayOfMonth()-1,true);}

运行结果:

        2.2 统计连续签到功能

        从最后一天开始往前累计直到遇到 0 的时候,统计出连续签到功能。先使用 BITFIELD 命令获取从本月 0 到当前月中的第 x 天的十进制数,再将该十进制数循环跟 1 进行与运算,当结果不为 0 的时候,累计次数;当结果为 0 的时候,跳出循环。

代码如下:

    @Testvoid text4(){coiledSign(1);}private void coiledSign(Integer userId){//获取当前时间LocalDateTime now = LocalDateTime.now();String date = now.format(DateTimeFormatter.ofPattern("yyyyMM:"));String key = "sign:"+ date +userId;List<Long> list = stringRedisTemplate.opsForValue().bitField(key,BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(now.getDayOfMonth())).valueAt(0));if (list == null || list.isEmpty()){return;}//拿到列表中第一个元素Long aLong = list.get(0);if (aLong == null || aLong == 0){return;}int count = 0;while (true){if ((aLong & 1) == 0){break;}else {count++;}aLong >>>= 1;}System.out.println("当前用户连续签到了:"+count+"次");}

运行结果:

        3.0 HyperLogLog 基本用法

        1)UV:全称 Unique Visitor,也叫独立访客量,是指通过互联网访问,浏览这个网页的自然人。1 天内同一个用户多次访问该网站,只记录 1 次。

        2)PV:全称 Page View,也叫页面访问量或点击量,用户没访问网站的一个页面,记录 1 次 PV,用户多次打开页面,则记录多次 PV。往往用来衡量网站的流量。

        UV 统计在服务端做会比较麻烦,因为要判断该用户是否已经统计过了,需要将统计过的用户信息保存。但是如果每个访问的用户都保存到 Redis 中,数据量会非常恐怖。

        而 HyperLogLog 是从 LogLog 算法派生的概率算法,用于确定非常大的集合的基数,而不需要存储其所有值。

        Redis 中的 HLL 是基于 string 结构实现的,单个 HLL 的内存永远小于 16 kb,内存占用低的令人发指!作为代价,其测量结果是概率性的,有小于 0.81% 的误差。不过对于 UV 统计来说,这完全可以忽略。

常见的命令:

        1)PFADD:在 key 中添加数据

PFADD key element [element ...]

        2)PFCOUNT:统计 key 中的数据量

PFCOUNT key [key ...]

        3)PFMERGE:合并两个 key 中的数据量

PFMERGE destkey sourcekey [sourcekey ...]

        3.1 测试使用 HyperLogLog 统计数据

        使用起来也很简单。每当用户来访问该网站的时候,都将当前用户 ID 都添加到 key 中,key 既可以设置为以天为单位、以月为单位等,如果用户 ID 重复来访问该网站,那么 HyperLogLog 会自动帮我们做了处理,完美符合 UV 的规则,因此使用 HyperLogLog 实现 UV 是非常合适的。

代码如下:

    @Testvoid text5(){//添加数据LocalDateTime now = LocalDateTime.now();String format = now.format(DateTimeFormatter.ofPattern("yyyy:MM:dd"));String key = "UV:"+ format;for (int i = 0; i < 5000; i++) {stringRedisTemplate.opsForHyperLogLog().add(key,"user"+i);}}@Testvoid text6(){//进行统计LocalDateTime now = LocalDateTime.now();String format = now.format(DateTimeFormatter.ofPattern("yyyy:MM:dd"));String key = "UV:"+ format;Long size = stringRedisTemplate.opsForHyperLogLog().size(key);System.out.println(size);}

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

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

相关文章

windows server2012 配制nginx安装为服务的时候,直接跳要安装.net框架,用自动的安装,直接失败的解决。

1、上一个已成功在安装过程中的图&#xff1a; 2、之前安装过程中错误的图&#xff1a; 3、离线安装解决&#xff1a; 下载.net framework 3.5&#xff0c;然后解压后&#xff0c;选择指定备用源路径&#xff0c;然后选择.net安装包所在目录&#xff1a; 只要指定上面全路径就…

4G模块点对点传输手把手教程!如何实现远程设备直接通信

使用4G模块进行点对点传输&#xff0c;可以实现远程设备的直接通信&#xff0c;广泛应用于工业控制、远程监控、物联网等领域。本教程将详细讲解如何通过4G模块&#xff0c;构建设备之间的点对点&#xff08;P2P&#xff09;传输系统&#xff0c;从配置设备、建立通信通道到实际…

Delphi Web和Web服务开发目前有哪些选择

Delphi Web和Web服务开发目前有哪些选择 Delphi Web和Web服务开发目前有以下几个选择&#xff1a; Delphi MVC Framework&#xff08;https://github.com/delphimvcframework/delphimvcframework&#xff09;&#xff1a;这是一个开源的Delphi Web框架&#xff0c;基于MVC&am…

【Linux】基本指令及其周边知识

1.准备阶段 在介绍Linux的基本指令之前&#xff0c;我先先向大家介绍一下我的Linux平台&#xff0c;首先我是在阿里云买了个服务器&#xff0c;然后使用Xshell来远程登录Linux&#xff0c;之后有关Linux上的操作都是在这上面进行的。如果你也买了相关的服务器并且设置了相关示…

Parallels Desktop19中文版2024九月最新

Parallels Desktop可以使轻松地在 MAC上运行成千上万款 Windows应用程序&#xff0c;如Excel&#xff0c;会计交易软件等。针对最新版 windows11和macOS Sonoma 进行优化。在 MAC虚拟机中跨多个操作系统开发和测试。包含 Parallels Toolbox – 40 多个适用于 Mac 和 PC 的一键…

ROS1录包偶现一次崩溃问题定位

现象&#xff1a;崩到了mogo_reporter里面 堆栈&#xff1a;crash里面同时存在两个主线程的堆栈 代码 #include "boost/program_options.hpp" #include <signal.h> #include <string> #include <sstream> #include <iostream> #include <…

[“1“, “2“, “3“].map(parseInt)结果

parseInt 的用法 parseInt 是 JavaScript 中的一个全局函数&#xff0c;用于将字符串转换为整数。它的基本语法如下&#xff1a; parseInt(string, radix);string&#xff1a;要解析的字符串。radix&#xff08;可选&#xff09;&#xff1a;字符串的基数&#xff0c;可以是 …

高科技企业选择跨网文件系统最容易踩坑的地方

在数字化时代&#xff0c;高科技企业频繁使用跨网文件交换系统的原因多种多样。首先&#xff0c;随着全球化的推进&#xff0c;企业需要在不同地理位置的分支机构之间传输敏感数据和重要文件。其次&#xff0c;跨网文件交换能够提高工作效率&#xff0c;确保信息的实时更新和共…

开源 TTS 模型「Fish Speech」1.4 发布;GameGen-O :生成开放世界游戏视频模型丨 RTE 开发者日报

开发者朋友们大家好&#xff1a; 这里是 「RTE 开发者日报」 &#xff0c;每天和大家一起看新闻、聊八卦。 我们的社区编辑团队会整理分享 RTE&#xff08;Real-Time Engagement&#xff09; 领域内「有话题的新闻」、「有态度的观点」、「有意思的数据」、「有思考的文章」、…

高并发下的生存之道:如何巧妙化解热Key危机?

我是小米,一个喜欢分享技术的29岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号“软件求生”,获取更多技术干货! 哈喽,大家好!我是小米,29岁,喜欢分享技术的小米上线啦!今天咱们来聊聊在互联网高并发场景下,一个让大家又爱又恨的问题——热Key问题。热Key是什么…

【C++】_stack和_queue容器适配器、_deque

当别人都在关注你飞的有多高的时候&#xff0c;只有父母在关心你飞的累不累。&#x1f493;&#x1f493;&#x1f493; 目录 ✨说在前面 &#x1f34b;知识点一&#xff1a;stack •&#x1f330;1.stack介绍 •&#x1f330;2.stack的基本操作 &#x1f34b;知识点二&…

【电路笔记】-反相运算放大器

反相运算放大器 文章目录 反相运算放大器1、概述2、理想反相运算放大器3、实际反相运算放大器3.1 闭环增益3.2 输入阻抗3.3 输出阻抗4、反相运算放大器示例5、总结1、概述 上一篇关于同相运算放大器的文章中已介绍了该运算放大器配置的所有细节,该配置在同相引脚 (+) 上获取输…

LSS如何创建视锥

1 完整代码 def create_frustum(self):# 128 352, 22 8in_H

LRELHLNNN;亲水性抗肝纤维化多肽作为基础肽;I型胶原蛋白靶向肽;九肽LRELHLNNN

【LRELHLNNN 简介】 LRELHLNNN是一种多肽&#xff0c;它能够选择性地结合到I型胶原蛋白&#xff0c;具有亲和力为170 nM。LRELHLNNN是由9个氨基酸组成&#xff0c;其氨基酸序列为H-Leu-Arg-Glu-Leu-His-Leu-Asn-Asn-Asn-OH。LRELHLNNN因其与I型胶原蛋白的高亲和力而在生物医学领…

MDC日志追踪(一)介绍

一、背景 在排查问题时&#xff0c;如果只根据关键字搜索&#xff0c;可能不精准&#xff0c;比如根据userId搜索&#xff0c;但是这个userId访问的记录也很多&#xff0c;很难定位出问题的是哪一次的&#xff1b;比如根据其他关键字搜索如orderId&#xff0c;可能很多用户都访…

wifi贴码推广能赚钱吗?wifi贴码怎么跟商家沟通?

大家好&#xff0c;我是鲸天科技千千&#xff0c;大家都知道我是做开发的&#xff0c;平时会给大家分享一些互联网相关的创业项目和网络技巧&#xff0c;感兴趣的可以给我点个关注。 最近WiFi这个项目很多朋友来问我&#xff0c;我是前两年就接触过这个&#xff0c;所以比较了…

“孪舟”引擎V5.0:更有颜、更真实、更智能、更灵活、更强大

在9月6日智汇云舟2024视频孪生产品发布会上&#xff0c;我们向线上线下嘉宾展示了基于视频孪生技术的众多产品&#xff0c;以及前沿技术。我们的目标是依托自研3DGIS引擎&#xff0c;将视频、AI、IoT等多种技术深度融合&#xff0c;升级数字孪生为视频孪生&#xff0c;实时实景…

《Putty 的下载和安装步骤》

Putty 是一款免费开源的 SSH 和 Telnet 客户端,它主要用于远程登录和管理其他计算机或服务器。 1.Putty 的一些主要特点和优势: 1. 简单易用:它具有直观的用户界面,操作相对简单,即使对于不太熟悉技术的用户也能轻松上手。 2. 支持多种协议:除了 SSH(Secure Shell)…

「漏洞复现」紫光电子档案管理系统 selectFileRemote SQL注入漏洞

0x01 免责声明 请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;作者不为此承担任何责任。工具来自网络&#xff0c;安全性自测&#xff0c;如有侵权请联系删…

亚马逊跨境新手小白如何选品?实操带练教程

选产品和找供应&#xff0c;是每个跨境人不可避免的&#xff0c;但是盲目的选品&#xff0c;无疑是大海捞针。最近我发现一个宝藏工具-店雷达&#xff0c;它可以帮助你选品的同时&#xff0c;同时还能为你筛选供应商。 我就分享一下我用店雷达的选品方法和思路&#xff0c;大家…