按需求选去重结构
去重方案的选择取决于三件事:是否需要判断单个元素是否存在、能接受多大误差、内存预算是多少。Set 精确但每个元素都有固定开销,Bitmap 适合稠密整数 ID,HyperLogLog 只输出近似基数且单键固定约 12KB,布隆过滤器能判断存在但存在误判。组合使用往往比单一方案更划算。
方案对比
| 方案 | 精确性 | 内存 | 适用 |
|---|---|---|---|
| Set | 精确 | 大,每个元素都有开销 | 中小基数且需要判断成员是否存在 |
| Bitmap | 精确 | 与最大 ID 成正比 | 稠密连续的整数 ID |
| HyperLogLog | 近似,约 0.81% 误差 | 每键不超过约 12KB | UV 类基数统计 |
| Bloom | 存在判定有误判 | 小 | 拦截不存在的查询与重复提交 |
| Hash 计数 | 精确 | 中等 | 需要记录出现次数的场景 |
四类命令对照
四类命令对照
SADD dedup:order:2026 O1 O2 # 精确:成员去重
SISMEMBER dedup:order:2026 O1 # 精确:判断是否已存在
SETBIT ua:20261012 1001 1 # 位图:适合稠密整数 ID
BITCOUNT ua:20261012 # 位图:统计活跃数量
PFADD uv:20261012 u1001 # 近似基数:内存固定
PFCOUNT uv:20261012 # 近似基数:输出估算值
BF.ADD bloom:user u1001 # 布隆过滤器,需 RedisBloom 模块
BF.EXISTS bloom:user u1001 # 存在判定,可能误判为存在典型使用场景
- 订单号去重:精确判重且基数不大时用 Set
- 日活统计:用户 ID 稠密连续时用位图
- 报表 UV:只要量级时用 HyperLogLog 省内存
- 重复提交拦截:布隆过滤器快速挡掉不存在的键
- 多级组合:先用布隆挡住大部分请求,再用 Set 精确判重
组合拳示例:查询先过布隆过滤器拦掉不存在的数据,命中后再用 Set 精确判重;报表侧 UV 用 HyperLogLog,兼顾内存与量级。
用 HyperLogLog 判断某个元素是否出现过是行不通的:它只提供基数估算,不支持单元素查询,需要成员判定就必须换结构。
落地顺序建议
- 先确认是否需要成员查询:不需要时优先考虑基数统计类结构
- 再在精确与内存之间取舍,量级越大越应倾向近似方案
- 布隆过滤器可能把不存在的元素判为存在,业务侧仍需精确校验兜底
- 去重键统一设置过期时间,避免按天累积后内存持续上涨