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

Kubernetes多实例下Redis相关方法的竞态问题求助

解决Kubernetes多实例下Redis分布式锁的竞态条件问题

问题根源分析

你的代码里的竞态条件主要来自这几个点:

  • redisService.checkExecute 和 redisService.startExecute 是两个独立操作,非原子性。两个实例可能同时通过check,然后都执行start,导致锁完全失效。
  • 递归调用重试没有退避策略,高并发下容易触发栈溢出,还会给Redis和服务带来额外压力。
  • 从Redis获取openGuestDTOList后,本地修改再写回的过程存在间隙,并发场景下会出现数据覆盖。
  • Collections.synchronizedList只能保证单进程内List操作的线程安全,跨实例的分布式并发场景下起不到作用。

修复方案

利用Redis的原子性操作实现可靠的分布式锁,结合Redisson客户端的原生锁能力(你代码里用了RMap,推测是Redisson),同时优化重试逻辑和数据更新方式:

  1. 用Redisson分布式锁替代自定义锁:Redisson的RLock自带原子性的加锁操作,支持过期时间避免死锁。
  2. 替换递归重试为带退避的循环:避免栈溢出,降低重试压力。
  3. 用Redisson的原子更新方法操作RMap:比如computeIfAbsent和compute,保证数据读写的原子性。
  4. 用try-finally确保锁释放:防止异常场景下锁无法释放。

修改后的代码示例

public void method(GuestDTO guestDTO) {
    String lockKey = "addingGuestToCache:" + guestDTO.getUser();
    RLock lock = redissonClient.getLock(lockKey);
    boolean acquired = false;
    
    // 带退避的循环重试,最多重试3次,每次间隔100ms
    int retryCount = 0;
    while (!acquired && retryCount < 3) {
        try {
            // 尝试加锁,最多等待1秒,锁自动过期5秒(避免死锁)
            acquired = lock.tryLock(1, 5, TimeUnit.SECONDS);
            if (acquired) {
                RMap<String, List<OpenGuestDTO>> openGuestDTOList = redisService.getOpenGuestDTOList();
                
                // 原子获取或创建用户的访客列表
                List<OpenGuestDTO> userGuestList = openGuestDTOList.computeIfAbsent(guestDTO.getUser(), 
                    k -> Collections.synchronizedList(new ArrayList<>()));
                
                // 检查访客是否已存在
                boolean exists = userGuestList.stream()
                    .anyMatch(g -> g.getGuestName().equalsIgnoreCase(guestDTO.getGuestName()));
                if (exists) {
                    return;
                }
                
                // 创建新访客并加入列表
                OpenGuestDTO openGuestDTO = new OpenGuestDTO();
                openGuestDTO.setGuestName(guestDTO.getGuestName());
                userGuestList.add(openGuestDTO);
            } else {
                retryCount++;
                Thread.sleep(100);
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException("获取锁被中断", e);
        } finally {
            if (acquired) {
                lock.unlock();
            }
        }
    }
    
    if (!acquired) {
        throw new RuntimeException("多次尝试后仍无法获取锁,请稍后重试");
    }
}

额外注意事项

  • 锁的过期时间要设置合理:既要足够完成业务操作,又不能太长导致死锁。如果业务操作耗时不确定,可以用Redisson的自动续期功能(默认开启)。
  • 避免锁粒度太粗:如果可以按用户+访客名加锁,能进一步减少锁竞争。
  • 异常处理要完善:比如Redis连接异常时的降级或告警逻辑。

内容的提问来源于stack exchange,提问作者doğukan elbasan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 03:01:39