登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  数据库 >  Redis

Redis Set 去重统计不准怎么办:从 SADD 返回值到 Key 粒度一步步排查

来源:17golang原创

时间:2026-06-15 12:58:44 194浏览 收藏

一个活动报名页显示“已报名 1250 人”,运营同学却说后台订单只有 1178 条。第一眼看像是缓存延迟,但我们不能直接下结论。Redis Set 做去重统计时,数量不准通常藏在三个地方:返回值理解错、Key 粒度混在一起、过期时间没处理好。

这篇文章我们从一个很小的现场开始,一步一步把问题拆开,看清楚 `SADD`、`SCARD` 和 Key 设计到底应该怎么配合。

摘要

Redis Set 适合保存“不重复成员”,常用于报名用户、接口请求编号、当天访问用户等场景。要让统计稳定,写入时要看 `SADD` 的新增结果,读取时用 `SCARD` 回查数量,同时按业务维度设计 Key,并给临时统计设置合理过期时间。

适合人群

适合正在用 Redis 做去重、UV、活动参与人数、幂等请求记录的后端同学。文章示例使用 Redis 命令和少量伪代码,不依赖特定语言框架。

目录

  • 问题现场:为什么显示数量比订单多
  • 先验证 SADD:它返回的不是总人数
  • 继续定位:Key 粒度混乱会把数据串起来
  • 修复方案:写入、统计、过期分开处理
  • 常见坑和最终检查

问题现场:为什么显示数量比订单多

我们先复现一个简单场景:活动 ID 是 9001,同一个用户可能重复点击报名按钮。业务希望“一个用户只算一次”,于是代码把用户 ID 写进 Redis Set。

SADD activity:join:9001 10001
SADD activity:join:9001 10002
SADD activity:join:9001 10001
SCARD activity:join:9001

按 Set 的语义,用户 `10001` 第二次写入不应该增加人数。如果页面仍然变多,我们就要先看写入逻辑是不是把每次点击都当成新增了。

Redis Set 去重统计问题现场:重复点击、SADD 写入、统计偏高、开始排查

先验证 SADD:它返回的不是总人数

第一轮猜测是:代码是不是误解了 `SADD` 的返回值?`SADD` 返回的是本次真正新增的成员数量,不是 Set 里的总成员数。

127.0.0.1:6379> SADD activity:join:9001 10001
(integer) 1
127.0.0.1:6379> SADD activity:join:9001 10001
(integer) 0
127.0.0.1:6379> SCARD activity:join:9001
(integer) 1

这段结果说明了两件事:第一次写入返回 `1`,代表新增成功;第二次写入返回 `0`,代表成员已经存在。要展示当前总人数,应该用 `SCARD`,不能把每一次请求都累加到一个普通数字字段里。

如果业务同时维护了一个 `join_count`,就很容易出现偏差:重复点击让计数器增加了,但 Set 实际人数没变。此时应以 Set 的数量为准,或者只在 `SADD` 返回 `1` 时再更新其他统计。

继续定位:Key 粒度混乱会把数据串起来

如果 `SADD` 返回值没用错,我们继续看 Key。很多统计不准不是命令错了,而是 Key 粒度太粗。比如把所有活动都写到同一个 Key:

SADD activity:join 9001:10001
SADD activity:join 9002:10001

这能去重,但统计某一个活动时就不方便,还容易在读取时切错维度。更清晰的做法是把活动 ID 放到 Key 里,把用户 ID 当成员。

SADD activity:join:9001 10001
SADD activity:join:9002 10001
SCARD activity:join:9001

这一步我们能定位到第二个常见原因:成员内容和 Key 维度混在一起,导致后续统计、清理、对账都变复杂。Key 负责业务范围,成员负责唯一对象,职责分开后排查会轻很多。

Redis Set Key 粒度排查:粗粒度 Key、活动维度、用户成员、单活动统计

修复方案:写入、统计、过期分开处理

现在我们把方案收拢。写入时只关心成员是否新增,统计时用 `SCARD`,临时活动结束后让 Key 自动过期。

SADD activity:join:9001 10001
EXPIRE activity:join:9001 604800
SCARD activity:join:9001

在业务代码里,可以把逻辑拆成三个动作:

added = SADD activity:join:{activityId} {userId}
if added == 1:
    写入报名记录或发送首次报名消息
count = SCARD activity:join:{activityId}

注意:如果活动 Key 已经存在,反复设置过期时间可能会延长活动窗口。更稳的写法是创建 Key 后设置一次过期,或者按活动结束时间计算剩余秒数,避免每天访问都把 Key 往后顺延。

Redis Set 修复流程:SADD 判断新增、SCARD 读总数、EXPIRE 管生命周期、对账验证

常见坑和最终检查

第一个坑是把 `SADD` 的返回值当总人数。它只能告诉你本次新增了几个成员,总人数要用 `SCARD`。

第二个坑是成员值不稳定。比如有的地方写用户 ID,有的地方写手机号,有的地方写 `user:10001`,这些在 Redis 看来都是不同成员,去重自然会失效。成员格式要统一。

第三个坑是大 Set 直接拉全量成员。统计人数用 `SCARD` 即可,不要为了数数量去读出所有成员。需要抽样排查时再用少量扫描,不要把全部成员一次性取回应用层。

最终检查可以按这四步走:

TYPE activity:join:9001
SCARD activity:join:9001
SISMEMBER activity:join:9001 10001
TTL activity:join:9001

如果类型是 `set`,数量和业务记录能对上,关键用户存在性正确,生命周期也符合活动结束时间,这个去重统计就基本稳住了。

总结

Redis Set 去重统计不准时,不要只盯着“Redis 有没有问题”。我们按现场一步步看,真正容易出错的是业务使用方式:把 `SADD` 返回值当总数、Key 粒度不清、成员格式不统一、过期策略顺延。

推荐落地规则很简单:Key 表示业务范围,成员表示唯一对象;新增看 `SADD`,总数看 `SCARD`,生命周期交给明确的过期策略。这样再遇到重复点击、重复请求或活动统计,就能少很多对账麻烦。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>