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统计
本博客为个人学习笔记,学习网站与详细见:黑马程序员Redis入门到实战 P88 - P95 目录 附近商铺 数据导入 功能实现 用户签到 签到功能 连续签到统计 UV统计 附近商铺 利用Redis中的GEO数据结构实现附近商铺功能,常见命令如下图所示。…...
小程序里面使用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%,会如何变化? none:延迟2秒间无色,3.8秒(0%-30%占1.8秒)前无色,之后变红到5秒绿最后蓝,动画结束时恢复初始(无色)。 forward:延迟2秒间无色&am…...
【C语言】条件运算符详解 - 《 A ? B : C 》
目录 C语言条件运算符详解1. 条件运算符的语法和使用示例 1:基本用法输出 2. 嵌套条件运算符示例 2:嵌套条件运算符输出 3. 条件运算符与 if-else 语句的比较示例 3:使用 if-else 语句示例 4:使用条件运算符 4. 条件运算符的实际应…...
乘积量化pq:将高维向量压缩 97%
向量相似性搜索在处理大规模数据集时,往往面临着内存消耗的挑战。例如,即使是一个包含100万个密集向量的小数据集,其索引也可能需要数GB的内存。随着数据集规模的增长,尤其是高维数据,内存使用量会迅速增加,…...
解决一下git clone失败的问题
1).不开梯子,我们用https克隆 git clone https://github.com 报错: Failed to connect to github.com port 443 after 2091 ms: Couldnt connect to server 解决办法: 开梯子,然后# 注意修改成自己的IP和端口号 gi…...
【 香橙派 AIpro评测】烧系统运行部署LLMS大模型跑开源yolov5物体检测并体验Jupyter Lab AI 应用样例(新手入门)
文章目录 一、引言⭐1.1下载镜像烧系统⭐1.2开发板初始化系统配置远程登陆💖 远程ssh💖查看ubuntu桌面💖 远程向日葵 二、部署LLMS大模型&yolov5物体检测⭐2.1 快速启动LLMS大模型💖拉取代码💖下载mode数据&#x…...
Azure Repos 仓库管理
从远端仓库克隆到本地 前提:本地要安装git,并且登录了账户 1.在要放这个远程仓库的路径下,打git 然后 git clone https://.. 如果要登录验证,那就验证下,点 generate git credentials,复制password 克隆完后,cd 到克隆的路径, 可以用 git branch -a //查看分…...
Day71 代码随想录打卡|回溯算法篇---全排列
题目(leecode T46): 给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。 方法:全排列是数学中的基础问题,也是回溯算法能解决的经典问题。全排列因为每个元素都会…...
开源科学工程技术软件
目录 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.为什么要进行端口映射呢?3.原理3.1【大白话】原理解释3.2 原理图 4.代码 1.端口映射的概念 端口映射(Port Mapping),也称为端口转发(P…...
HTC 10 刷系统 LineageOS 19.1 Android 12
解锁手机 解锁或导致数据全部清除,注意保存 Bootloader解锁,S-ON可以不用解锁(好像可以绕过解锁安装twrp,暂时没尝试) HTC 官方 Unlock Bootloader HTC Desire 20 pro 可以不通过官方网站解锁 adb reboot bootload…...
访问者模式(Visitor Pattern)
访问者模式(Visitor Pattern) 定义 访问者模式(Visitor Pattern) 表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素类的前提下定义作用于这些元素的新操作。 该模式的主要思想是将作用于某种数据结构中的各…...
mac如何查看cpu和显卡温度
在Mac上查看CPU和显卡温度,你可以使用以下几种方法: 方法1:使用内建工具“活动监视器” 虽然“活动监视器”不能直接显示温度信息,但它可以显示CPU使用情况等信息。 打开“活动监视器”,可以通过以下路径找到&#…...
MongoDB教程(六):mongoDB复制副本集
💝💝💝首先,欢迎各位来到我的博客,很高兴能够在这里和您见面!希望您在这里不仅可以有所收获,同时也能感受到一份轻松欢乐的氛围,祝你生活愉快! 文章目录 引言一、MongoD…...
牛客小白月赛98 (个人题解)(补全)
前言: 昨天晚上自己一个人打的小白月赛(因为准备数学期末已经写烦了),题目难度感觉越来越简单了(不在像以前一样根本写不了一点,现在看题解已经能看懂一点了),能感受到自己在不断进步…...
Ubuntu压缩解压各类型文件
在Ubuntu系统中,解压不同格式的压缩文件可能需要安装不同的工具。以下是一些常见的压缩格式和相应的安装命令: ZIP文件: 工具:unzip 安装命令: sudo apt install unzip 解压命令 unzip filename.zip 如果需要保留目录…...
昇思学习打卡-20-生成式/GAN图像生成
文章目录 网络介绍生成器和判别器的博弈过程数据集可视化模型细节训练过程网络优缺点优点缺点 网络介绍 GAN通过设计生成模型和判别模型这两个模块,使其互相博弈学习产生了相当好的输出。 GAN模型的核心在于提出了通过对抗过程来估计生成模型这一全新框架。在这个…...
javafx、node js、socket、OpenGL多线程
机器学习、算法、人工智能、汇编(mips、arm、8086)、操作系统、数据挖掘、编译原理、计算机网络、Arena软件、linux xv6、racket、shell、Linux、PHP、Haskell、Scala、spark、UML、mathematica、GUI、javafx、node js、socket、OpenGL、多线程、qt、数据…...
Appium+python自动化(十六)- ADB命令
简介 Android 调试桥(adb)是多种用途的工具,该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具,其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利,如安装和调试…...
2.Vue编写一个app
1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...
微信小程序 - 手机震动
一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注:文档 https://developers.weixin.qq…...
九天毕昇深度学习平台 | 如何安装库?
pip install 库名 -i https://pypi.tuna.tsinghua.edu.cn/simple --user 举个例子: 报错 ModuleNotFoundError: No module named torch 那么我需要安装 torch pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple --user pip install 库名&#x…...
免费数学几何作图web平台
光锐软件免费数学工具,maths,数学制图,数学作图,几何作图,几何,AR开发,AR教育,增强现实,软件公司,XR,MR,VR,虚拟仿真,虚拟现实,混合现实,教育科技产品,职业模拟培训,高保真VR场景,结构互动课件,元宇宙http://xaglare.c…...
Razor编程中@Html的方法使用大全
文章目录 1. 基础HTML辅助方法1.1 Html.ActionLink()1.2 Html.RouteLink()1.3 Html.Display() / Html.DisplayFor()1.4 Html.Editor() / Html.EditorFor()1.5 Html.Label() / Html.LabelFor()1.6 Html.TextBox() / Html.TextBoxFor() 2. 表单相关辅助方法2.1 Html.BeginForm() …...
怎么让Comfyui导出的图像不包含工作流信息,
为了数据安全,让Comfyui导出的图像不包含工作流信息,导出的图像就不会拖到comfyui中加载出来工作流。 ComfyUI的目录下node.py 直接移除 pnginfo(推荐) 在 save_images 方法中,删除或注释掉所有与 metadata …...
Unity中的transform.up
2025年6月8日,周日下午 在Unity中,transform.up是Transform组件的一个属性,表示游戏对象在世界空间中的“上”方向(Y轴正方向),且会随对象旋转动态变化。以下是关键点解析: 基本定义 transfor…...
Linux基础开发工具——vim工具
文章目录 vim工具什么是vimvim的多模式和使用vim的基础模式vim的三种基础模式三种模式的初步了解 常用模式的详细讲解插入模式命令模式模式转化光标的移动文本的编辑 底行模式替换模式视图模式总结 使用vim的小技巧vim的配置(了解) vim工具 本文章仍然是继续讲解Linux系统下的…...
CppCon 2015 学习:Reactive Stream Processing in Industrial IoT using DDS and Rx
“Reactive Stream Processing in Industrial IoT using DDS and Rx” 是指在工业物联网(IIoT)场景中,结合 DDS(Data Distribution Service) 和 Rx(Reactive Extensions) 技术,实现 …...
