incr 能扛高并发因服务端原子执行读取→加1→写回,不依赖锁;但要求 key 值必须为整数字符串,否则报错,且需严格隔离维度、首次递增时设过期、避免类型污染。

INCR 命令为什么能扛住高并发?
因为 INCR 在 Redis 服务端是原子执行的:读取 → 加 1 → 写回,整个过程不可分割。它不依赖锁,也不靠客户端协调——单线程模型天然保证了这一点。哪怕同时有 5000 个请求打到同一个 key,最终结果也一定是精确的 5000。
但前提是:INCR 只认整数字符串。如果 key 当前存的是 "{\"status\":\"ok\"}" 或空字符串 "",就会直接报错:ERR value is not an integer or out of range。
- Redis 会自动把不存在的 key 当作
"0"处理,所以不用提前SET key 0 - 底层用的是
int编码(值在 long 范围内时),内存开销极小,没有 SDS 字符串头开销 - 不要用
GET+ 客户端计算 +SET,这彻底放弃原子性,高并发下必然出错
计数器 key 设计容易踩的坑
key 混用是最隐蔽的问题之一。比如把用户数据和计数器都塞进 user:1001:先 SET user:1001 "{\"name\":\"Alice\"}",再 INCR user:1001 就立刻失败。
推荐做法是强制维度隔离:
- 按业务拆分前缀:
page:view:20260713、api:rate:192.168.1.100 - 按用途加后缀:
order:counter、order:data,一眼区分类型 - 测试环境务必清空残留 key,尤其注意值为
"init"、"test"这类非数字字符串的 key
带过期时间的计数器怎么设才安全?
INCR 和 EXPIRE 分两步调用不是原子的。如果 INCR 成功但 EXPIRE 失败(网络中断、进程崩溃),key 就永久存在了。
Redis 8.2.3 是一款安全优先的高性能键值存储系统。该版本紧急修复了可能引发远程代码执行(RCE)的高危漏洞(CVE-2025-62507),并解决了 HyperLogLog 及 Cuckoo Filter 等数据结构在特定场景下的崩溃问题。建议所有用户立即升级,以保障生产环境的系统稳定与数据安全。
正确姿势是“首次递增时才设过期”:
- 用
INCR获取返回值,如果等于1,说明这是该 key 第一次被创建,此时立即EXPIRE key 3600 - Java 中用
redisTemplate.opsForValue().increment(key)后,必须手动判断返回值是否为1L,再调expire(key, 3600, TimeUnit.SECONDS) - 避免用
SET key 0 EX 3600初始化,因为SET和EX不是原子组合
INCRBY 和 DECR 能随便用吗?
不能。INCRBY、DECR、DECRBY 全部继承 INCR 的约束:目标 key 的 value 必须是合法整数字符串。它们不是“容错命令”,也不会帮你转类型或兜底。
典型误用:
- 对刚用
SET user:profile '{"age":25}'存的 key 直接调INCRBY user:profile 1→ 报错 - 用
APPEND给计数器 key 追加内容,导致后续所有INCR类命令失效 - 跨语言写入不一致:Go 用
strconv.Itoa()写整数,Python 用json.dumps(123)写成"123"—— 看似一样,但 JSON 字符串带引号,INCR会拒绝
真正关键的不是命令多强大,而是从第一次写入开始就守住 value 类型边界。一旦污染,只能 DEL 重建。










