计数器与热点分摊
INCR 与 INCRBY 是 Redis 最常用的原子计数原语:键不存在时按 0 处理,操作在服务端单线程内完成,因此天然不会出现读改写丢失更新。局限不在原子性而在热点:一条计数键始终由同一个节点处理,在分片集群里就是单节点负载上限。高并发计数有两种常用解法,把计数拆成多个子键再求和,或把多个维度合并进一个 Hash 降低键数量。
基本信息
| 基础命令 | INCR key 加一,INCRBY key n 加指定整数,INCRBYFLOAT 支持小数 |
|---|---|
| 存储编码 | 纯整数值的 String 使用 int 编码,内存开销很小 |
| 溢出行为 | 按 64 位有符号整数回绕,需要上限保护时应自行判断 |
| 生命周期 | 计数键通常需要配合 EXPIRE 控制,否则会长期累积 |
| 读取 | GET 单键取值,MGET 批量取多个子键后由应用求和 |
典型使用场景
- 页面访问量与接口调用次数统计,按天建键并设置过期时间
- 库存扣减与配额控制:INCR 后判断是否超限,超限则回退
- 限流计数:固定窗口内累加,窗口到期自动重置
- 任务进度跟踪:多个工作节点各自累加已完成数量,汇总方读取总量
- 排行榜的原始分累加:先用计数键累积,定时写入 ZSet 生成榜单
基础与分片计数
基础与分片计数
redis-cli INCR page:view:home # 加一并返回新值
redis-cli EXPIRE page:view:home 86400 # 按天过期,避免键无限累积
redis-cli INCRBY page:view:home 10
# 分片计数:写入时按随机数分散到多个子键
redis-cli INCR page:view:home:{shard1}
redis-cli INCR page:view:home:{shard2}
redis-cli MGET page:view:home:{shard1} page:view:home:{shard2} # 应用侧求和
redis-cli GET page:view:home几种计数方案对照
| 方案 | 优点 | 代价 |
|---|---|---|
| 单键 INCR | 实现最简单,读值无额外步骤 | 单键热点,分片集群里受单节点限制 |
| 拆分子键再求和 | 写入压力分散到多个节点 | 读取需合并,存在一致性窗口 |
| Hash 合并维度 | 键数量大幅下降,字段级原子递增 | 单键规模上升,需要注意编码与阻塞 |
| 位图或 HLL | 内存极省,适合去重与基数 | 只能给近似或布尔结果,无法精确累加 |
分片计数的读取语义:多条子键分别递增,读取时求和得到的是「某个时刻的近似总量」,求和过程中发生的写入可能落在窗口之外。对总额精度要求高的场景应把定期落库的持久值作为对账依据,Redis 计数只作实时展示。
给未知用途的键补 EXPIRE 前先确认它是纯计数键:误给业务主键设置过期时间会造成数据静默消失,且这类操作难以回滚。规范做法是计数键统一命名前缀,便于安全地批量治理 TTL。