Java Redis 怎么基于 String 结构实现高并发场景下的点赞功能

舞姬之光

舞姬之光

2026-08-01

700人浏览

原创

redis string 不适合单独实现点赞功能,因其无法原子性地完成“判断用户是否已点赞+更新计数”操作,易导致重复点赞和并发计数不准;应结合 set 记录用户状态,并用 lua 脚本保障原子性。

java redis 怎么基于 string 结构实现高并发场景下的点赞功能

用 Redis 的 String 类型实现点赞功能,在高并发下容易出错,核心问题在于“重复点赞”和“并发计数不准”。String 本身不支持原子性增减与条件判断,直接用 INCRSET 无法解决“用户是否已点过”的判断逻辑。所以单纯靠 String 并不适合做完整点赞功能——但可以配合其他结构或策略来用,关键在于设计边界和取舍。

为什么 String 不适合单独承载点赞逻辑

Redis String 是键值对,适合存单个数值(比如总点赞数),但它无法记录“谁点了”,也无法在一次操作中完成“检查 + 更新”。例如:

  • 先用 GET user:like:post:123 查是否点过 → 再决定是否 INCR post:likes:123:中间存在竞态,两个请求可能同时读到 null,都执行 incr,导致重复计数。
  • SETNX 记录用户状态(如 user:like:post:123:uid456 值为 1)→ 可防重复,但总点赞数还得额外维护,且大量用户会产生海量 key。

推荐方案:String + Set 组合(实用且易落地)

把“总点赞数”交给 String,“用户是否点过”交给 Set,利用 Redis 原子命令协同工作:

  • SADD like:post:123 uid456 添加用户 ID 到集合:天然去重,返回 1 表示新增成功(即首次点赞),0 表示已存在。
  • 只有 SADD 返回 1 时,才执行 INCR post:likes:123 更新总赞数。
  • 取消点赞则用 SREM like:post:123 uid456 + DECR post:likes:123(注意需判断 SREM 是否真删掉了元素)。

Java 示例(Lettuce 客户端):

Java JDK 25
Java JDK 25

Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。

下载
public boolean likePost(Long postId, Long userId) {
    String likeSetKey = "like:post:" + postId;
    String likeCountKey = "post:likes:" + postId;
<pre class="brush:java;toolbar:false;">// 原子添加用户到集合,返回是否新增
Long added = syncCommands.sadd(likeSetKey, String.valueOf(userId));
if (added == 1L) {
    syncCommands.incr(likeCountKey);
    return true; // 点赞成功
}
return false; // 已点过

}

进阶优化:用 Lua 脚本保证原子性

上面 Java 代码中 SADD 和 INCR 是两次网络调用,极端情况下仍有微小窗口(比如 SADD 成功后服务宕机,INCR 未执行)。更稳妥的做法是把整个逻辑写进 Lua 脚本,在 Redis 端原子执行:

-- like.lua
local likeSetKey = KEYS[1]
local likeCountKey = KEYS[2]
local userId = ARGV[1]
<p>local added = redis.call("SADD", likeSetKey, userId)
if added == 1 then
redis.call("INCR", likeCountKey)
return 1
else
return 0
end</p>

Java 中加载并执行:

DefaultScript script = DefaultScript.script(luaLikeScript);
Long result = syncCommands.eval(script, ReturnType.INTEGER,
    Arrays.asList("like:post:123", "post:likes:123"),
    String.valueOf(456));

注意事项与边界处理

  • 内存成本:每个帖子一个 Set,用户量大时需评估内存。可考虑定期归档冷帖的 like Set,或用 HyperLogLog 估算去重数(但不支持取消点赞)。
  • 数据一致性:如果业务要求强一致,建议搭配数据库双写(Redis 更新成功后再异步落库),并增加补偿任务校验。
  • 缓存穿透:对非法 postId 查询,加布隆过滤器或空值缓存,避免打穿 Redis 和 DB。
  • 点赞数回源:初始点赞数可从 DB 加载到 Redis,用 SETNX 防覆盖;后续只更新 Redis,定时或按需同步回 DB。

不复杂但容易忽略:String 在点赞场景里最稳的用法,就是老老实实当“计数器”,别让它背“判断逻辑”的锅。把职责拆清楚,再用原子命令或 Lua 把它们串起来,高并发下的点赞就能既快又准。

Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南

相关专题

更多
java
java

Java是一个通用术语,用于表示Java软件及其组件,包括“Java运行时环境 (JRE)”、“Java虚拟机 (JVM)”以及“插件”。php中文网还为大家带了Java相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

2023.06.15

3864

6

java正则表达式语法
java正则表达式语法

java正则表达式语法是一种模式匹配工具,它非常有用,可以在处理文本和字符串时快速地查找、替换、验证和提取特定的模式和数据。本专题提供java正则表达式语法的相关文章、下载和专题,供大家免费下载体验。

2023.07.05

2839

9

java自学难吗
java自学难吗

Java自学并不难。Java语言相对于其他一些编程语言而言,有着较为简洁和易读的语法,本专题为大家提供java自学难吗相关的文章,大家可以免费体验。

2023.07.31

2876

8

java配置jdk环境变量
java配置jdk环境变量

Java是一种广泛使用的高级编程语言,用于开发各种类型的应用程序。为了能够在计算机上正确运行和编译Java代码,需要正确配置Java Development Kit(JDK)环境变量。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

2023.08.01

637

3

java保留两位小数
java保留两位小数

Java是一种广泛应用于编程领域的高级编程语言。在Java中,保留两位小数是指在进行数值计算或输出时,限制小数部分只有两位有效数字,并将多余的位数进行四舍五入或截取。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

2023.08.02

602

3

java基本数据类型
java基本数据类型

java基本数据类型有:1、byte;2、short;3、int;4、long;5、float;6、double;7、char;8、boolean。本专题为大家提供java基本数据类型的相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.02

706

5

java有什么用
java有什么用

java可以开发应用程序、移动应用、Web应用、企业级应用、嵌入式系统等方面。本专题为大家提供java有什么用的相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.02

1317

5

java在线网站
java在线网站

Java在线网站是指提供Java编程学习、实践和交流平台的网络服务。近年来,随着Java语言在软件开发领域的广泛应用,越来越多的人对Java编程感兴趣,并希望能够通过在线网站来学习和提高自己的Java编程技能。php中文网给大家带来了相关的视频、教程以及文章,欢迎大家前来学习阅读和下载。

2023.08.03

18845

3

配置java环境变量
配置java环境变量

配置Java环境变量是为了让操作系统能够识别和使用Java的相关命令和功能。本专题为大家提供配置java环境变量相关文章,帮助大家解决问题。

2023.08.03

647

8

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Java JDBC数据库连接官方教程
Java JDBC数据库连接官方教程

共0课时 | 0人学习

Java 26官方文档
Java 26官方文档

共0课时 | 0人学习