You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Redis Cache处理计数器访问竞态条件相关问题咨询

问题解答

认知纠正

你对Redis自增/自减功能的理解存在偏差。Redis的INCR/DECR系列命令就是专门为「业务唯一标识 + 整数计数器」的场景设计的,完全支持你需要的二元结构:直接将你的业务标识(比如代码中的url)作为Redis的key,计数器数值作为该key对应的字符串类型value即可,命令执行时会自动将value解析为整数完成原子增减操作,并非仅在新建key时生效。

Redis内置计数器特性

Redis针对计数器场景的原子操作特性如下:

  • 所有INCR/DECR/INCRBY/DECRBY命令都是原子执行的,即使多个客户端同时操作同一个key,也不会出现竞态条件
  • 执行DECR key时如果对应key不存在,会自动将key的value初始化为0后再执行减1操作,返回最终计算后的数值

适配业务场景的落地方案

你的业务逻辑是:每个url对应初始5次访问额度,每次访问扣减1次,额度大于0时允许访问,不存在的url自动初始化额度。可以直接用以下两种方案实现,完全不需要自行加锁:

方案1:可预枚举所有url的场景

如果业务中用到的url可以提前枚举,直接在Redis中初始化所有url对应的value为5,后续每次请求直接执行DECR your_url:

  • 若返回结果 ≥ 0:表示扣减成功,允许访问
  • 若返回结果 < 0:表示额度用尽,拒绝访问

方案2:url动态生成的场景

如果url是动态生成的无法预初始化,可以直接在Redis端执行原子Lua脚本实现完整逻辑,整个脚本执行过程是原子的,不会产生竞态问题:

-- 传入参数:KEYS[1] 为当前请求的url
local current = redis.call('GET', KEYS[1])
-- key不存在,初始化额度为5,扣减1次后剩余4次,允许访问
if current == false then
    redis.call('SET', KEYS[1], 4)
    return 1
end
current = tonumber(current)
-- 剩余额度大于0,扣减后允许访问
if current > 0 then
    redis.call('DECR', KEYS[1])
    return 1
end
-- 额度用尽,拒绝访问
return 0

在Java代码中调用该Lua脚本,根据返回值判断即可:返回1设置allow=true,返回0设置allow=false。

方案优势

和你现有的synchronized+数据库的实现方案对比,Redis方案有以下优势:

  • 天然规避竞态:不需要自行实现锁逻辑,Redis原子操作从底层避免了并发问题
  • 支持分布式部署:synchronized是进程级锁,多实例部署时会失效,Redis方案只要对接同一个Redis集群,无论部署多少实例逻辑都安全
  • 性能大幅提升:Redis内存操作的响应速度远快于数据库读写,高并发场景下表现更稳定

内容的提问来源于stack exchange,提问作者Tony

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 20:54:01