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 可忽略不计

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

牛客TOP101:合并两个排序的链表

文章目录 1. 题目描述2. 解题思路3. 代码实现 1. 题目描述 2. 解题思路 与正常的合并两个有序数组思路一样&#xff0c;这里可以定义一个头节点&#xff08;虚拟节点&#xff09;&#xff0c;可以方便我们一开始进行连接。用两个指针标记两个链表的结点&#xff0c;进行循环比较…

5.4 软件工程-系统设计

系统设计 - 概述 设计软件系统总体结构 数据结构及数据库设计 编写概要设计文档、评审 详细设计的基本任务 真题

FPGA 项目菜单功能比较

为了帮助您更好地理解每个FPGA功能模块的实用场合、区别和特点&#xff0c;以下是详细的比较&#xff1a; 功能模块实用场合区别特点FPGA I/O自动控制系统、数据采集系统直接与FPGA板卡上的物理端口交互&#xff0c;配置和使用外部I/O设备灵活配置输入输出端口&#xff0c;支持…

[Spring] Spring Web MVC案例实战

&#x1f338;个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 &#x1f3f5;️热门专栏: &#x1f9ca; Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm1001.2014.3001.5482 &#x1f355; Collection与…

「Pytorch」roLabelImg 图像异常旋转 bug

在进行Yolo-obb 模型训练的时候需要标注旋转框&#xff0c;roLabelImg 是比较推荐的一款旋转框标注工具&#xff0c;既可以标注正常的矩形框&#xff0c;还可以标注旋转框 roLabelImg Github 地址&#xff1a;https://github.com/HumanSignal/labelImg 但是在使用过程中遇到了…

数据库管理-第218期 服务器内存(20240711)

数据库管理218期 2024-07-11 数据库管理-第218期 服务器内存&#xff08;20240711&#xff09;1 内存2 ECC内存3 原理3.1 多副本传输3.2 纠错码3.3 汉明码 总结 数据库管理-第218期 服务器内存&#xff08;20240711&#xff09; 作者&#xff1a;胖头鱼的鱼缸&#xff08;尹海文…

利用OSMnx进行城市路网数据的速度与通行时间推算及分析

本文还是以广州市路网为例&#xff0c;通过osmmx调用ox.add_edge_speeds(G)时&#xff0c;该函数会遍历图G 中的每条边&#xff08;即每条街道&#xff09;&#xff0c;并基于一些预设的规则或街道属性&#xff08;如街道类型、是否为主要道路、是否有速度限制等&#xff09;来…

Logistic回归算法原理详解及应用

目录 引言 基本原理 损失函数 参数估计 优缺点 应用 Logistic回归优化算法 具体案例 引言 逻辑回归&#xff08;Logistic Regression&#xff09;是一种广泛用于分类问题的统计方法&#xff0c;尤其是二分类问题。尽管名字中有“回归”二字&#xff0c;但它实际上是一…

Vue3总结汇总(不间断更新)

一、常用依赖插件汇总 1.1 依赖 UI组件库:1、AntDesignVue2、ElementPlus 网络请求&#xff1a;axios 组件通信&#xff1a;mitt ID生成&#xff1a;nanoid 路由&#xff1a;vue-router 三维&#xff1a;cesium 构建工具:Vite vite-plugin-vue-setup-extend&#xff1a;简化set…

编译打包自己的云手机(redroid)镜像

前言 香橙派上跑云手机可以看之前的文章&#xff1a; 香橙派5plus上跑云手机方案一 redroid(带硬件加速)香橙派5plus上跑云手机方案二 waydroid 还有一个cuttlefish方案没说&#xff0c;后面再研究&#xff0c;cuttlefish的优势在于可以自定义内核且selinux是开启的&#xf…

推荐5个实用的可视化工具

面对海量的数据&#xff0c;我们应该如何高效地提取其价值&#xff0c;让复杂的信息一目了然&#xff1f;这正是可视化工具大显身手的舞台。今天&#xff0c;我就来分享几款非常好用的数据可视化工具&#xff0c;它们不仅能够帮助你轻松驾驭数据&#xff0c;还能让你的工作汇报…

HCIE是什么等级的证书?

HCIE&#xff08;华为认证互联网专家&#xff0c;Huawei Certified Internetwork Expert&#xff09;是华为认证体系中的最高等级证书。它要求考生具备在复杂网络环境中规划、设计、部署、运维和优化网络的能力。HCIE认证是华为认证体系中最具挑战性和含金量的认证之一&#xf…

鸿蒙语言基础类库:【@ohos.application.testRunner (TestRunner)】 测试

TestRunner TestRunner模块提供了框架测试的能力。包括准备单元测试环境、运行测试用例。 如果您想实现自己的单元测试框架&#xff0c;您必须继承这个类并覆盖它的所有方法。 说明&#xff1a; 开发前请熟悉鸿蒙开发指导文档&#xff1a;gitee.com/li-shizhen-skin/harmony-…

浪潮天启防火墙TQ2000远程配置方法SSL-V偏、L2xx 配置方法

前言 本次设置只针对配置V偏&#xff0c;其他防火墙配置不涉及。建议把防火墙内外网都调通后再进行V偏配置。 其他配置可参考&#xff1a;浪潮天启防火墙配置手册 配置SSLVxx 在外网端口开启SSLVxx信息 开启SSLVxx功能 1、勾选 “启用SSL-Vxx” 2、设置登录端口号&#xff0…

使用Apache服务部署静态网站

前言&#xff1a;本博客仅作记录学习使用&#xff0c;部分图片出自网络&#xff0c;如有侵犯您的权益&#xff0c;请联系删除 目录 一、网站服务程序 ​二、配置服务文件参数 ​三、SELinux安全子系统 四、个人用户主页功能 ​五、虚拟网站主机功能 六、Apache的访问控制…

CSA笔记1-基础知识和目录管理命令

[litonglocalhost ~]$ 是终端提示符&#xff0c;类似于Windows下的cmd的命令行 litong 当前系统登录的用户名 分隔符 localhost 当前机器名称&#xff0c;本地主机 ~ 当前用户的家目录 $ 表示当前用户为普通用户若为#则表示当前用户为超级管理员 su root 切换root权限…

计算器原生js

目录 1.HTML 2.CSS 2.JS 4.资源 5.运行截图 6.下载连接 7.注意事项 1.HTML <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-s…

pytorch学习(六):卷积层的使用

卷积函数的概念 卷积核从输入特征图的左上角开始&#xff0c;按照设定的步长&#xff08;Stride&#xff09;滑动。步长决定了卷积核每次滑动的像素数&#xff0c;这里我们假设步长 s1。在每次滑动时&#xff0c;卷积核与输入特征图对应位置的元素相乘&#xff0c;然后将这些乘…

Linux C语言基础 day9

目录 思维导图 学习目标&#xff1a; 学习内容&#xff1a; 1. 值传递与地址传递&#xff08;非常重要&#xff09; 1.1 值传递 1.2 地址传递 2. 递归函数 2.1 递归的概念 2.2 递归条件 2.3 递归思想 3. 指针 3.1 指针相关概念 3.2 指针变量的定义 3.2.1. 定义格…

对于GPT-5在一年半后发布的期待!

首先&#xff0c;如果GPT-5真如OpenAI首席技术官米拉穆拉蒂&#xff08;Mira Murati&#xff09;在采访中所透露的那样&#xff0c;在一年半后发布&#xff0c;并在某些领域达到博士级的智能&#xff0c;这无疑将是一个令人振奋的消息。这一预测不仅反映了AI技术的快速发展&…