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

Redis实战—附近商铺、用户签到、UV统计

  本博客为个人学习笔记,学习网站与详细见:黑马程序员Redis入门到实战 P88 - P95

目录

附近商铺

数据导入 

功能实现

用户签到

签到功能

连续签到统计 

UV统计


附近商铺

利用Redis中的GEO数据结构实现附近商铺功能,常见命令如下图所示。 

key值由特定前缀与商户类型id组成,每个GEO存储一个店铺id与该店铺的经纬度信息,如下图所示。


数据导入 

编写单元测试,将MySql数据库中的所有商铺位置信息导入Redis中,代码如下。

@Test
void loadShopData() {// 1.查询所有店铺信息List<Shop> shops = shopService.list();// 2.将店铺按照typeId分组,typeId一致的放到一个集合中Map<Long, List<Shop>> map = shops.stream().collect(Collectors.groupingBy(Shop::getTypeId));// 3.分批完成写入Redisfor (Map.Entry<Long, List<Shop>> entry : map.entrySet()) {// 3.1 获取类型idLong typeId = entry.getKey();String key = "shop:geo:" + typeId;// 3.2 获取同类型的店铺集合List<Shop> list = entry.getValue();// 3.3 写入redis( GEOADD key 经度 纬度 member)List<RedisGeoCommands.GeoLocation<String>> locations = new ArrayList<>(list.size());for (Shop shop : list) {locations.add(new RedisGeoCommands.GeoLocation<>(shop.getId().toString(),new Point(shop.getX(), shop.getY())));}stringRedisTemplate.opsForGeo().add(key, locations);}
}

功能实现

由于SpringDataRedis的2.3.9版本并不支持Redis 6.2提供的GEOSEARCH命令,因此我们需要修改版本,代码如下。

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId><exclusions><exclusion><artifactId>spring-data-redis</artifactId><groupId>org.springframework.data</groupId></exclusion><exclusion><artifactId>lettuce-core</artifactId><groupId>io.lettuce</groupId></exclusion></exclusions>
</dependency>
<dependency><groupId>org.springframework.data</groupId><artifactId>spring-data-redis</artifactId><version>2.6.2</version>
</dependency>
<dependency><groupId>io.lettuce</groupId><artifactId>lettuce-core</artifactId><version>6.1.6.RELEASE</version>
</dependency>

Controller层代码如下。

@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);
}

接口方法的具体实现代码如下。

@Override
public Result queryShopByType(Integer typeId, Integer current, Double x, Double y) {// 1.判断是否需要根据坐标查询if (x == null || y == null) {// 不需要坐标查询,按数据库查询Page<Shop> page = query().eq("type_id", typeId).page(new Page<>(current, SystemConstants.DEFAULT_PAGE_SIZE));// 返回数据return Result.ok(page.getRecords());}// 2.计算分页参数int from = (current - 1) * SystemConstants.DEFAULT_PAGE_SIZE;int end = current * SystemConstants.DEFAULT_PAGE_SIZE;// 3.查询redis、按照距离排序、分页。结果:shopId,distanceString key = "shop:geo:" + typeId;GeoResults<RedisGeoCommands.GeoLocation<String>> results = stringRedisTemplate.opsForGeo().search(key,GeoReference.fromCoordinate(x, y),new Distance(5000),RedisGeoCommands.GeoSearchCommandArgs.newGeoSearchArgs().includeDistance().limit(end));// 4.解析出idif (results == null)return Result.ok(Collections.emptyList());List<GeoResult<RedisGeoCommands.GeoLocation<String>>> list = results.getContent();// 如果没有下一页,则结束if (list.size() < from)return Result.ok(Collections.emptyList());// 4.1 截取从from~end的部分ArrayList<Object> ids = new ArrayList<>(list.size());Map<String, Distance> distanceMap = new HashMap<>(list.size());list.stream().skip(from).forEach(result -> {// 4.2 获取店铺idString shopIdStr = result.getContent().getName();ids.add(Long.valueOf(shopIdStr));// 4.2 获取距离Distance distance = result.getDistance();distanceMap.put(shopIdStr, distance);});// 5.根据id查询ShopString idStr = StrUtil.join(",", ids);List<Shop> shops = query().in("id", ids).last("ORDER BY FIELD(id," + idStr + ")").list();for (Shop shop : shops) {shop.setDistance(distanceMap.get(shop.getId().toString()).getValue());}// 6.返回return Result.ok(shops);
}

用户签到

签到功能


Controller层代码如下。 

@PostMapping("/sign")
public Result sign() {return userService.sign();
}

接口方法的具体实现代码如下。

@Override
public Result sign() {// 1.获取当前登录用户信息Long userId = UserHolder.getUser().getId();// 2.获取当前日期LocalDateTime now = LocalDateTime.now();// 3.拼接keyString keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));String key = "sign:" + userId + keySuffix;// 4.计算今天是本月的第几天int dayOfMonth = now.getDayOfMonth();// 5.写入Redis SETBIT key offset// true:写入1// false:写入0stringRedisTemplate.opsForValue().setBit(key, dayOfMonth - 1, true);return Result.ok();
}

连续签到统计 


Controller层代码如下。

@GetMapping("/sign/count")
public Result signCount() {return userService.signCount();
}

接口方法的具体实现代码如下。

@Override
public Result signCount() {// 1.获取当前登录用户信息Long userId = UserHolder.getUser().getId();// 2.获取当前日期LocalDateTime now = LocalDateTime.now();// 3.拼接keyString keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));String key = "sign:" + userId + keySuffix;// 4.计算今天是本月的第几天int dayOfMonth = now.getDayOfMonth();// 5.获取本月截止今天为止的所有签到记录,返回结果是一个十进制数字 BITFIELD sign:5:202203 GET u14 0List<Long> result = stringRedisTemplate.opsForValue().bitField(key,BitFieldSubCommands.create() //创建子命令.get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0)//选择子命令);if (result == null || result.isEmpty())return Result.ok(0);// 获取本月签到位图Long num = result.get(0);if (num == null || num == 0)return Result.ok(0);// 6.循环遍历int cnt = 0;//记录连续签到天数while (true) {if ((num & 1) == 0)break;num >>= 1;cnt++;}return Result.ok(cnt);
}

UV统计


测试
我们直接利用单元测试,向HyperLogLog中添加100万条数据,看看统计效果如何,测试代码如下。

@Test
void testHyperLogLog() {// 准备数组,装用户数据String[] users = new String[1000];// 数组角标int index = 0;for (int i = 1; i <= 1000000; i++) {// 赋值users[index++] = "user_" + i;// 每1000条发送一次if (i % 1000 == 0) {index = 0;stringRedisTemplate.opsForHyperLogLog().add("hll1", users);}}// 统计数量Long size = stringRedisTemplate.opsForHyperLogLog().size("hll1");System.out.println("size =" + size);
}

测试结果如下图所示。

误差 = 1 - (997593 / 1000000)≈ 0.002 可忽略不计

相关文章:

Redis实战—附近商铺、用户签到、UV统计

本博客为个人学习笔记&#xff0c;学习网站与详细见&#xff1a;黑马程序员Redis入门到实战 P88 - P95 目录 附近商铺 数据导入 功能实现 用户签到 签到功能 连续签到统计 UV统计 附近商铺 利用Redis中的GEO数据结构实现附近商铺功能&#xff0c;常见命令如下图所示。…...

小程序里面使用vant ui中的vant-field组件,如何使得输入框自动获取焦点

//.wxml <van-fieldmodel:value"{{ userName }}"placeholder"请输入学号"focus"{{focusUserName}}"/>// .js this.setData({focusUserName: true});vant-field...

Html_Css问答集(12)

99、将上例的0%改为30%&#xff0c;会如何变化&#xff1f; none:延迟2秒间无色&#xff0c;3.8秒&#xff08;0%-30%占1.8秒&#xff09;前无色&#xff0c;之后变红到5秒绿最后蓝&#xff0c;动画结束时恢复初始&#xff08;无色&#xff09;。 forward:延迟2秒间无色&am…...

【C语言】条件运算符详解 - 《 A ? B : C 》

目录 C语言条件运算符详解1. 条件运算符的语法和使用示例 1&#xff1a;基本用法输出 2. 嵌套条件运算符示例 2&#xff1a;嵌套条件运算符输出 3. 条件运算符与 if-else 语句的比较示例 3&#xff1a;使用 if-else 语句示例 4&#xff1a;使用条件运算符 4. 条件运算符的实际应…...

乘积量化pq:将高维向量压缩 97%

向量相似性搜索在处理大规模数据集时&#xff0c;往往面临着内存消耗的挑战。例如&#xff0c;即使是一个包含100万个密集向量的小数据集&#xff0c;其索引也可能需要数GB的内存。随着数据集规模的增长&#xff0c;尤其是高维数据&#xff0c;内存使用量会迅速增加&#xff0c…...

解决一下git clone失败的问题

1&#xff09;.不开梯子&#xff0c;我们用https克隆 git clone https://github.com 报错&#xff1a; Failed to connect to github.com port 443 after 2091 ms: Couldnt connect to server 解决办法&#xff1a; 开梯子&#xff0c;然后# 注意修改成自己的IP和端口号 gi…...

【 香橙派 AIpro评测】烧系统运行部署LLMS大模型跑开源yolov5物体检测并体验Jupyter Lab AI 应用样例(新手入门)

文章目录 一、引言⭐1.1下载镜像烧系统⭐1.2开发板初始化系统配置远程登陆&#x1f496; 远程ssh&#x1f496;查看ubuntu桌面&#x1f496; 远程向日葵 二、部署LLMS大模型&yolov5物体检测⭐2.1 快速启动LLMS大模型&#x1f496;拉取代码&#x1f496;下载mode数据&#x…...

Azure Repos 仓库管理

从远端仓库克隆到本地 前提:本地要安装git,并且登录了账户 1.在要放这个远程仓库的路径下,打git 然后 git clone https://.. 如果要登录验证,那就验证下,点 generate git credentials,复制password 克隆完后,cd 到克隆的路径, 可以用 git branch -a //查看分…...

Day71 代码随想录打卡|回溯算法篇---全排列

题目&#xff08;leecode T46&#xff09;&#xff1a; 给定一个不含重复数字的数组 nums &#xff0c;返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。 方法&#xff1a;全排列是数学中的基础问题&#xff0c;也是回溯算法能解决的经典问题。全排列因为每个元素都会…...

开源科学工程技术软件

目录 0 参考链接 1 Silx 2 Klampt 3 参数化三维3D软件Dune 3D 4 GPS日志文件查看器GPXSee 5 三维3D软件Chili3D 6 集成电路设计软件XicTools 7 天文学软件Cosmonium 8 计算流体力学软件FluidX3D 9 点云处理软件CloudCompare 10 野外火灾建模软件WindNinja 11 电子设…...

甄选范文“论软件维护方法及其应用”软考高级论文,系统架构设计师论文

论文真题 软件维护是指在软件交付使用后,直至软件被淘汰的整个时间范围内,为了改正错误或满足 新的需求而修改软件的活动。在软件系统运行过程中,软件需要维护的原因是多种多样的, 根据维护的原因不同,可以将软件维护分为改正性维护、适应性维护、完善性维护和预防性 维护…...

【服务器】端口映射

文章目录 1.端口映射的概念1.1 端口映射的类型1.2 端口映射的应用场景1.3 示例 2.为什么要进行端口映射呢&#xff1f;3.原理3.1【大白话】原理解释3.2 原理图 4.代码 1.端口映射的概念 端口映射&#xff08;Port Mapping&#xff09;&#xff0c;也称为端口转发&#xff08;P…...

HTC 10 刷系统 LineageOS 19.1 Android 12

解锁手机 解锁或导致数据全部清除&#xff0c;注意保存 Bootloader解锁&#xff0c;S-ON可以不用解锁&#xff08;好像可以绕过解锁安装twrp&#xff0c;暂时没尝试&#xff09; HTC 官方 Unlock Bootloader HTC Desire 20 pro 可以不通过官方网站解锁 adb reboot bootload…...

访问者模式(Visitor Pattern)

访问者模式&#xff08;Visitor Pattern&#xff09; 定义 访问者模式&#xff08;Visitor Pattern&#xff09; 表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素类的前提下定义作用于这些元素的新操作。 该模式的主要思想是将作用于某种数据结构中的各…...

mac如何查看cpu和显卡温度

在Mac上查看CPU和显卡温度&#xff0c;你可以使用以下几种方法&#xff1a; 方法1&#xff1a;使用内建工具“活动监视器” 虽然“活动监视器”不能直接显示温度信息&#xff0c;但它可以显示CPU使用情况等信息。 打开“活动监视器”&#xff0c;可以通过以下路径找到&#…...

MongoDB教程(六):mongoDB复制副本集

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; 文章目录 引言一、MongoD…...

牛客小白月赛98 (个人题解)(补全)

前言&#xff1a; 昨天晚上自己一个人打的小白月赛&#xff08;因为准备数学期末已经写烦了&#xff09;&#xff0c;题目难度感觉越来越简单了&#xff08;不在像以前一样根本写不了一点&#xff0c;现在看题解已经能看懂一点了&#xff09;&#xff0c;能感受到自己在不断进步…...

Ubuntu压缩解压各类型文件

在Ubuntu系统中&#xff0c;解压不同格式的压缩文件可能需要安装不同的工具。以下是一些常见的压缩格式和相应的安装命令&#xff1a; ZIP文件&#xff1a; 工具&#xff1a;unzip 安装命令&#xff1a; sudo apt install unzip 解压命令 unzip filename.zip 如果需要保留目录…...

昇思学习打卡-20-生成式/GAN图像生成

文章目录 网络介绍生成器和判别器的博弈过程数据集可视化模型细节训练过程网络优缺点优点缺点 网络介绍 GAN通过设计生成模型和判别模型这两个模块&#xff0c;使其互相博弈学习产生了相当好的输出。 GAN模型的核心在于提出了通过对抗过程来估计生成模型这一全新框架。在这个…...

javafx、node js、socket、OpenGL多线程

机器学习、算法、人工智能、汇编&#xff08;mips、arm、8086&#xff09;、操作系统、数据挖掘、编译原理、计算机网络、Arena软件、linux xv6、racket、shell、Linux、PHP、Haskell、Scala、spark、UML、mathematica、GUI、javafx、node js、socket、OpenGL、多线程、qt、数据…...

【学习笔记】无人机(UAV)在3GPP系统中的增强支持(七)-通过无人机实现无线接入的独立部署

引言 本文是3GPP TR 22.829 V17.1.0技术报告&#xff0c;专注于无人机&#xff08;UAV&#xff09;在3GPP系统中的增强支持。文章提出了多个无人机应用场景&#xff0c;分析了相应的能力要求&#xff0c;并建议了新的服务级别要求和关键性能指标&#xff08;KPIs&#xff09;。…...

模糊综合评价

对多因素影响的事务的评价&#xff08;如人才&#xff0c;方案&#xff0c;成果&#xff09;&#xff0c;有时难以给出影响的确切表达&#xff0c;此时可以采取模糊综合评价的方法。 该方法可以对人&#xff0c;事&#xff0c;物进行比较全面而又定量化的评价。 实例1&#xff…...

系统测试-白盒测试学习

目录 1、语句覆盖法&#xff1a; 2、判定覆盖法&#xff1a; 3、条件覆盖法&#xff1a; 4、判定条件覆盖&#xff1a; 5、条件组合的覆盖&#xff1a; 6、路径覆盖&#xff1a; 黑盒&#xff1a;需求 白盒&#xff1a;主要用于单元测试 1、语句覆盖法&#xff1a; 程序…...

UI设计工具选择指南:Sketch、XD、Figma、即时设计

在数字产品设计产业链中&#xff0c;UI设计师往往起着连接前后的作用。产品经理从一个“需求”开始&#xff0c;制定一个抽象的产品概念原型。UI设计师通过视觉呈现将抽象概念具体化&#xff0c;完成线框图交互逻辑视觉用户体验&#xff0c;最终输出高保真原型&#xff0c;并将…...

Pycharm 导入 conda 环境

使用时经常在此处卡壳&#xff0c;在此做个记录。 这个位置选择 conda 安装路径下的 python.exe 文件即可...

Vue封装Tooltip(提示工具)

<template> <div class"tooltip" mouseover"showTooltip" mouseleave"hideTooltip"> <slot></slot> <!-- 使用slot来接收传入的内容 --> <span class"tooltiptext" v-if"visible">{…...

Go 1.19.4 函数-Day 08

1. 函数概念和调用原理 1.1 基本介绍 函数是基本的代码块&#xff0c;用于执行一个任务。 Go 语言最少有个 main() 函数。 你可以通过函数来划分不同功能&#xff0c;逻辑上每个函数执行的是指定的任务。 函数声明告诉了编译器函数的名称&#xff0c;返回类型&#xff0c;和参…...

Docker-Nvidia(NVIDIA Container Toolkit)

安装NVIDIA Container Toolkit工具&#xff0c;支持docker使用GPU 目录 1.NVIDIA Container Toolkit 安装1.1 nvidia-docker安装1.2 验证1.2.1 验证安装1.2.2 额外补充 1.NVIDIA Container Toolkit 安装 1.1 nvidia-docker安装 NVIDIA/nvidia-docker Installing the NVIDIA …...

Mongodb 3.6 数据恢复操作

一、安装MongoDB 忽略 二、创建账号和授权 在新的MongoDB上创建用户管理员。先切换到admin库&#xff0c;然后通过命令创建数据库管理员账号和密码。最后再验证账号密码是否创建成功&#xff01; use admin db.createUser({user:"root",pwd:"123456Ab",…...

C++ | Leetcode C++题解之第238题除自身以外数组的乘积

题目&#xff1a; 题解&#xff1a; class Solution { public:vector<int> productExceptSelf(vector<int>& nums) {int length nums.size();// L 和 R 分别表示左右两侧的乘积列表vector<int> L(length, 0), R(length, 0);vector<int> answer(l…...