必须用 stringredistemplate.opsforvalue().increment() 实现原子计数,因其底层调用 redis 原子 incr 命令;需解决原子性、过期、并发和 key 冲突四问题,避免手动 get+set 导致覆盖、静态 key 引发热点、未设 ttl 造成内存泄漏、超限溢出及连接异常未降级。

直接用 StringRedisTemplate.opsForValue().increment() 就能实现,但必须处理好原子性、过期、并发和 key 冲突这四个问题,否则线上会丢数或计错。
为什么不能直接用 set + get 手动加一?
因为 get 和 set 是两个独立操作,中间可能被其他线程/节点覆盖。比如两个请求同时读到值为 100,各自加 1 后都写回 101,实际只+1 而非+2。
Redis 的 INCR 命令是原子的,StringRedisTemplate.opsForValue().increment() 底层就调用它,这才是安全起点。
常见错误现象:redisTemplate.opsForValue().get(key) 后手动 +1 再 set —— 这种写法在分布式环境下必然出错。
increment() 的 key 设计必须带业务上下文
单纯用 "counter" 这种静态 key,所有请求都挤在一个 key 上,既无法区分维度,又容易成为热点。
应该按实际场景拼接唯一 key:
- 用户级 PV 统计 →
"pv:uid:" + userId - 接口限流计数 →
"limit:" + userId + ":" + methodName - 订单号生成 →
"seq:order:" + LocalDate.now()
注意:key 中避免含特殊字符(如空格、斜杠),否则可能触发 Redis 解析异常;建议统一用冒号分隔,小写 + 数字组合。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
必须显式设置过期时间,否则 key 永久残留
increment() 不会自动设 TTL,第一次调用后 key 永久存在,除非手动 expire。
正确做法是:首次递增时同步设过期,用 opsForValue().increment(key) 返回值判断是否为 1(即首次创建),再调用 expire(key, duration, unit):
long current = redisTemplate.opsForValue().increment(key);
if (current == 1) {
redisTemplate.expire(key, 24, TimeUnit.HOURS);
}
不这么做会导致 Redis 内存持续增长,尤其高频 key(如每秒千次请求)可能几天就积攒几百万无效 key。
高并发下 increment() 本身没问题,但要注意返回值类型和溢出
increment() 返回 Long,但 Redis 的 INCR 实际支持最大值是 9223372036854775807(Long.MAX_VALUE)。超过会直接报错 ERR value is not an integer or out of range。
如果业务需要超大计数(比如千万级日活统计),别硬扛,改用 HINCRBY 存到 Hash 结构里,或者拆成多个 key 轮询递增。
另一个坑:Spring Boot 3.x 默认用 Lettuce 客户端,increment() 在连接异常时抛 RedisConnectionFailureException,必须捕获并降级(比如本地 AtomicInteger + 日志告警),不能让整个接口因 Redis 不可用而失败。










