Redis --- 使用zset处理排行榜和计数问题
在处理计数业务时,我们一般会使用一个数据结构,既是集合又可以保证唯一性,所以我们会选择Redis中的set集合:
业务逻辑:
用户点击点赞按钮,需要再set集合内判断是否已点赞,未点赞则需要将点赞数+1并保存用户信息到集合中,已点赞则需要将数据库点赞数-1并移除set集合中的用户。
@Service
public class BlogServiceImpl extends ServiceImpl<BlogMapper, Blog> implements IBlogService {@Autowiredprivate IUserService userService;@Resourceprivate StringRedisTemplate stringRedisTemplate;@Overridepublic Result likeBlog(Long id) {// 获取登录用户Long userId = UserHolder.getUser().getId();// 判断当前登录用户是否已经点赞String key = "blog:like:" + id;Boolean isMember = stringRedisTemplate.opsForSet().isMember(key, userId.toString());if(BooleanUtil.isFalse(isMember)){// 未点赞// 数据库点赞数+1boolean isSuccess = update().setSql("like = like + 1").eq("id",id).update();// 保存用户到Redis集合中if(isSuccess){stringRedisTemplate.opsForSet().add(key, userId.toString());}} else {// 已点赞,取消点赞// 数据库点赞数-1boolean isSuccess = update().setSql("like = like - 1").eq("id",id).update();// 移除set集合中的用户stringRedisTemplate.opsForSet().remove(key, userId.toString());}return Result.ok();}
}
那么我们想要实现按照点赞时间的先后顺序排序,返回Top5的用户,这个时候set无法保证数据有序,所以我们需要换一个数据结构满足业务需求:

Redis 的 ZSET(有序集合) 是一个非常适合用于处理 排行榜 和 计数问题 的数据结构。在高并发的点赞业务中,使用 ZSET 可以帮助我们高效地管理点赞的排名,并且由于 ZSET 的排序特性,我们可以轻松实现根据点赞数实时排序的功能。
ZSET 数据结构
Redis 的 ZSET 是一个集合,它的每个元素都会关联一个 分数(score),这个分数决定了元素在集合中的排序。ZSET 保证集合中的元素是按分数排序的,并且可以在 O(log(N)) 的时间复杂度内进行添加、删除和查找操作。
在高并发的点赞业务中,ZSET 可以帮助我们轻松地进行以下几项操作:
- 记录每个用户对某个内容(如文章、评论等)的点赞数。
- 通过分数进行实时排序,获取点赞数最多的内容。
优化高并发的点赞操作
在高并发情况下,当多个用户同时对某个内容进行点赞时,我们需要高效地更新该内容的点赞数,并保证数据一致性。ZSET 提供了很好的支持,具体步骤如下:
- 用户点赞操作:使用
ZINCRBY命令来对某个元素的分数进行增量操作,表示对该内容的点赞数增加。- 查看点赞数:可以通过
ZSCORE命令获取某个内容的当前点赞数。- 查看排行榜:使用
ZRANGE或ZREVRANGE命令来获取点赞数排名前 N 的内容,按分数进行排序。
ZSET 结构设计
key:表示某个内容的点赞的 id。
value:表示点赞用户的 id。
score:根据点赞时间排序。
下面是修改后的点赞逻辑:
@Service
public class BlogServiceImpl extends ServiceImpl<BlogMapper, Blog> implements IBlogService {@Autowiredprivate IUserService userService;@Resourceprivate StringRedisTemplate stringRedisTemplate;@Overridepublic Result likeBlog(Long id) {// 获取登录用户Long userId = UserHolder.getUser().getId();// 判断当前登录用户是否已经点赞String key = "blog:like:" + id;Double score = stringRedisTemplate.opsForZSet().score(key, userId.toString());if(score == null){// 未点赞// 数据库点赞数+1boolean isSuccess = update().setSql("like = like + 1").eq("id",id).update();// 保存用户到Redis集合中if(isSuccess){stringRedisTemplate.opsForZSet().add(key, userId.toString(), System.currentTimeMillis());}} else {// 已点赞,取消点赞// 数据库点赞数-1boolean isSuccess = update().setSql("like = like - 1").eq("id",id).update();// 移除set集合中的用户stringRedisTemplate.opsForZSet().remove(key, userId.toString());}return Result.ok();}
}
而点赞排行榜代码如下:
@Service
public class BlogServiceImpl extends ServiceImpl<BlogMapper, Blog> implements IBlogService {@Autowiredprivate IUserService userService;@Resourceprivate StringRedisTemplate stringRedisTemplate;@Overridepublic Result queryBlogLikes(Long id) {String key = "blog:like:" + id;// 查询top5的点赞用户 zrange key 0 4Set<String> top5 = stringRedisTemplate.opsForZSet().range(key, 0, 4);if (top5 == null || top5.isEmpty()) {return Result.ok(Collections.emptyList());}// 解析出集合中的用户的idList<Long> ids = top5.stream().map(Long::valueOf).collect(Collectors.toList());// 根据id查询用户,并将类型由User转为UserDTO,随后转换为List集合String idStr = StrUtil.join(",",ids);
// List<UserDTO> userDTOs = userService.listByIds(ids).stream()
// .map(user -> BeanUtil.copyProperties(user, UserDTO.class))
// .collect(Collectors.toList());List<UserDTO> userDTOs = userService.query().in("id",ids).last("order by field(id," + idStr +")").list().stream().map(user -> BeanUtil.copyProperties(user, UserDTO.class)).collect(Collectors.toList());return Result.ok(userDTOs);}
}
使用
userService.query().in("id", ids).last("order by field(id," + idStr + ")")来查询用户信息,并且使用order by field(id, ...)语句来保证查询结果的顺序与top5中的用户顺序一致。这里的
order by field(id, ...)是关键,它确保了从数据库返回的数据顺序和 Redis 返回的top5用户顺序完全匹配。因为 Redis 中的 ZSet 是有顺序的,top5会按照点赞数量进行排序。如果直接使用listByIds方法,可能会导致结果顺序不一致。
相关文章:
Redis --- 使用zset处理排行榜和计数问题
在处理计数业务时,我们一般会使用一个数据结构,既是集合又可以保证唯一性,所以我们会选择Redis中的set集合: 业务逻辑: 用户点击点赞按钮,需要再set集合内判断是否已点赞,未点赞则需要将点赞数1…...
响应式编程_04Spring 5 中的响应式编程技术栈_WebFlux 和 Spring Data Reactive
文章目录 概述响应式Web框架Spring WebFlux响应式数据访问Spring Data Reactive 概述 https://spring.io/reactive 2017 年,Spring 发布了新版本 Spring 5, Spring 5 引入了很多核心功能,这其中重要的就是全面拥抱了响应式编程的设计思想和实…...
C++ Primer 算术运算符
欢迎阅读我的 【CPrimer】专栏 专栏简介:本专栏主要面向C初学者,解释C的一些基本概念和基础语言特性,涉及C标准库的用法,面向对象特性,泛型特性高级用法。通过使用标准库中定义的抽象设施,使你更加适应高级…...
中位数定理:小试牛刀> _ <2025牛客寒假1
给定数轴上的n个点,找出一个到它们的距离之和尽量小的点(即使我们可以选择不是这些点里的点,我们还是选择中位数的那个点最优) 结论:这些点的中位数就是目标点。可以自己枚举推导(很好想) (对于 点的数量为…...
一些常用的HTML结构
1. 页面基本结构 DOCTYPE 声明: 作用:告知浏览器使用哪种 HTML 版本进行解析。示例: <!DOCTYPE html><html> 标签: 作用:作为整个 HTML 文档的根元素,包含文档的头部和主体。示例࿱…...
js的 encodeURI() encodeURIComponent() decodeURI() decodeURIComponent() 笔记250205
js的 encodeURI() encodeURIComponent() decodeURI() decodeURIComponent() 在JavaScript中,处理URI编码和解码的四个关键函数为:encodeURI()、encodeURIComponent()、decodeURI()和decodeURIComponent()。它们分别用于不同的场景,具体区别和…...
安全实验作业
一 拓扑图 二 要求 1、R4为ISP,其上只能配置IP地址;R4与其他所有直连设备间均使用共有IP 2、R3-R5-R6-R7为MGRE环境,R3为中心站点; 3、整个OSPF环境IP基于172.16.0.0/16划分; 4、所有设备均可访问R4的环回&#x…...
《Python预训练视觉和大语言模型》:从DeepSeek到大模型实战的全栈指南
就是当代AI工程师的日常:* - 砸钱买算力,却卡在分布式训练的“隐形坑”里; - 跟着论文复现模型,结果连1/10的性能都达不到; - 好不容易上线应用,却因伦理问题被用户投诉…… 当所有人都在教你怎么调用…...
血压计OCR文字检测数据集VOC+YOLO格式2147张11类别
数据集格式:Pascal VOC格式YOLO格式(不包含分割路径的txt文件,仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数):2147 标注数量(xml文件个数):2147 标注数量(txt文件个数):2147 …...
Java 面试合集(2024版)
种自己的花,爱自己的宇宙 目录 第一章-Java基础篇 1、你是怎样理解OOP面向对象??? 难度系数:? 2、重载与重写区别??? 难度系数:? 3、接口与抽象类的区别??? 难度系数:? 4、深拷贝与浅拷贝的理解??? 难度系数&…...
Typora免费使用
一.下载地址 https://typoraio.cn/ 二.修改配置文件 1.找到安装路径下的LicenseIndex.180dd4c7.4da8909c.chunk.js文件 文件路径为:安装路径\resources\page-dist\static\js\LicenseIndex.180dd4c7.4da8909c.chunk.js 将js中的 e.hasActivated"true"e.hasActiva…...
第一性原理:游戏开发成本的思考
利润 营收-成本 营收定价x销量x分成比例 销量 曝光量x 点击率x (购买率- 退款率) 分成比例 100%- 平台抽成- 税- 引擎费- 发行抽成 成本开发成本运营成本 开发成本 人工外包办公地点租金水电设备折旧 人工成本设计成本开发成本迭代修改成本后续内容…...
裁员潮血洗硅谷,普通人惨遭裁员的血泪教训——要随时做好失业的准备
我大学室友21年暑假在meta的某AI组实习过,压力巨大!组里大群天天消息99,年底实习结束直接就进到Google去了,听说eng组的intern十有八九都拿到了return offer,但都利用空余时间跳到了别的大厂。 离谱的时候,…...
MacBook Pro(M1芯片)Qt环境配置
MacBook Pro(M1芯片)Qt环境配置 1、准备 试图写一个跨平台的桌面应用,此时想到了使用Qt,于是开始了搭建开发环境~ 在M1芯片的电脑上安装,使用brew工具比较方便 Apple Silicon(ARM/M1…...
智能编码在前端研发的创新应用
一、前端开发实例 今天主要想分享一些关于大模型如何协助我们进行前端编码的实践。首先,让我们以一个前端开发的实例开始。通常,当需要实现一个新的前端功能时,我们会收到相关的背景和需求描述。我的期望是,大模型能够直接使用这…...
[特殊字符] ChatGPT-4与4o大比拼
🔍 ChatGPT-4与ChatGPT-4o之间有何不同?让我们一探究竟! 🚀 性能与速度方面,GPT-4-turbo以其优化设计,提供了更快的响应速度和处理性能,非常适合需要即时反馈的应用场景。相比之下,G…...
在Spring Cloud中将Redis共用到Common模块
前言 在分布式系统中,共用组件的设计可以极大地提升代码复用性和维护性。Spring Cloud中将Redis共用到一个公共模块(common模块)是一个常见的设计实践,这样可以让多个微服务共享相同的Redis配置和操作逻辑。本文将详细介绍如何在…...
基于RK3588/RK3576+MCU STM32+AI的储能电站电池簇管理系统设计与实现
伴随近年来新型储能技术的高质量规模化发展,储能电站作为新能源领域的重要载体, 旨在配合逐步迈进智能电网时代,满足电力系统能源结构与分布的创新升级,给予相应规模 电池管理系统的设计与实现以新的挑战。同时,电子系…...
Django框架丨从零开始的Django入门学习
Django 是一个用于构建 Web 应用程序的高级 Python Web 框架,Django是一个高度模块化的框架,使用 Django,只要很少的代码,Python 的程序开发人员就可以轻松地完成一个正式网站所需要的大部分内容,并进一步开发出全功能…...
稀疏混合专家架构语言模型(MoE)
注:本文为 “稀疏混合专家架构语言模型(MoE)” 相关文章合辑。 手把手教你,从零开始实现一个稀疏混合专家架构语言模型(MoE) 机器之心 2024年02月11日 12:21 河南 选自huggingface 机器之心编译 机器之心…...
spring基础总结
先修知识:依赖注入,反转控制,生命周期 IDEA快捷键 Ctrl Altm:提取方法,设置trycatch 通用快捷键: Ctrl F:在当前文件中查找文本。Ctrl R:在当前文件中替换文本。Ctrl Z:撤销…...
【C#】Process、ProcessStartInfo启动外部exe
在C#中使用 Process 和 ProcessStartInfo 类启动外部 .exe 文件,可以按照以下步骤进行: 创建 ProcessStartInfo 实例:配置进程启动信息,包括可执行文件的路径、传递给该程序的参数等。 设置启动选项:根据需要配置 Pro…...
【实用小技巧】如何不更新application.yml而更新spring的配置
大家都知道,我们在java工程中,常常在application.yml中有各种各样的运行时的配置,一般来说都是这样的结构: a:b:c: {ENV_NAME} 这样,我们在部署应用时,就可以通过在不同的局点修改ENV_NAME的值࿰…...
windows linux常用基础命令
windows基础命令 cd …/ (访问D盘 直接D: 进入目录cd…\baidudu) color 2 改变颜色 dir 浏览当前目录中有什么内容 例如 dir windows可以浏览windows中有什么文件 cls 清屏 cd windows 可以跳转到c盘目录的下面 cd…/可以返回到上一级目录 ./当前目录 cd \ 直…...
openRv1126 AI算法部署实战之——TensorFlow TFLite Pytorch ONNX等模型转换实战
Conda简介 查看当前系统的环境列表 conda env list base为基础环境 py3.6-rknn-1.7.3为模型转换环境,rknn-toolkit版本V1.7.3,python版本3.6 py3.6-tensorflow-2.5.0为tensorflow模型训练环境,tensorflow版本2.5.0,python版本…...
java进阶1——JVM
java进阶——JVM 1、JVM概述 作用 Java 虚拟机就是二进制字节码的运行环境,负责装载字节码到其内部,解释/编译为对 应平台上的机器码指令行,每一条 java 指令,java 虚拟机中都有详细定义,如怎么取操 作数,…...
基于深度学习的视觉检测小项目(十六) 用户管理界面的组态
分组和权限: 用户分为三个组,管理员、普通用户、访客。 • 管理员的权限和作业范围: 添加和删除用户、更改所有用户的信息(用户名、登录密码、所在分组等)、查看和备份以及复制数据库; • 普通用户的权限和…...
Docker使用指南(一)——镜像相关操作详解(实战案例教学,适合小白跟学)
目录 1.镜像名的组成 2.镜像操作相关命令 镜像常用命令总结: 1. docker images 2. docker rmi 3. docker pull 4. docker push 5. docker save 6. docker load 7. docker tag 8. docker build 9. docker history 10. docker inspect 11. docker prune…...
《2025,AI重塑世界进行时》
开年爆点,AI 浪潮再掀高潮 2025 年开年,AI 领域便热闹非凡,热点事件不断,让人深刻感受到这股科技浪潮正以汹涌之势奔腾而来。先是深度求索公司(DeepSeek)的 DeepSeek - R1 模型横空出世,迅速在国…...
visual studio安装
一、下载Visual Studio 访问Visual Studio官方网站。下载 Visual Studio Tools - 免费安装 Windows、Mac、Linux 在主页上找到并点击“下载 Visual Studio”按钮。 选择适合需求的版本,例如“Visual Studio Community”(免费版本)&#x…...
