20250217 随笔 redis非原子性操作简述

从你提供的文本来看,核心是 Redis 作为缓存的检查机制,以及非原子性操作导致的不一致性问题。
我们可以拆解为两个部分来理解:


📌 1. 逻辑:先查 Redis,再决定是否注册

逻辑流程

  1. 先查询 Redis 是否有某个 key(可能是用户注册标识、验证码等)。
  2. 如果 Redis 中有数据,则直接返回(避免重复操作)。
  3. 如果 Redis 中没有数据,则执行新的注册,并存入 Redis,设置60秒过期时间

示例代码

String key = "user:register:" + phoneNumber;
String cachedValue = redisTemplate.opsForValue().get(key);

if (cachedValue == null) {  // Redis 无数据,执行注册逻辑
    // 执行新用户注册
    userService.register(phoneNumber);
    
    // 将 key 存入 Redis,防止重复注册
    redisTemplate.opsForValue().set(key, "registered", 60, TimeUnit.SECONDS);
} else {
    System.out.println("该用户已注册");
}

⏳ 作用

  • 通过 Redis 缓存查询结果,降低数据库查询次数,提高性能。
  • 避免并发重复注册。

📌 2. 非原子性操作,导致数据不一致

这一部分说的是 多个 Redis 操作不是原子的,可能会导致数据不一致问题,尤其是在并发情况下

⚠ 问题:非原子性操作

Redis 并不自动保证多个操作的原子性。如果有多个线程同时执行:

  1. A 线程查询 Redis,发现 key 不存在(缓存失效)。
  2. B 线程也查询 Redis,发现 key 也不存在(同样缓存失效)。
  3. A 和 B 线程都开始执行注册逻辑(导致重复注册)。
  4. A 和 B 线程都将数据写入 Redis,但只有一个生效,另一个可能会被覆盖。
🛠 解决方案
  1. 使用 SETNX 实现分布式锁

    • SETNX(SET if Not eXists)可以确保只有一个线程可以写入 key,避免并发问题:
    Boolean success = redisTemplate.opsForValue().setIfAbsent(key, "registered", 60, TimeUnit.SECONDS);
    if (Boolean.TRUE.equals(success)) {
        userService.register(phoneNumber);
    } else {
        System.out.println("该用户已注册");
    }
    

    作用

    • 只有第一个线程能成功写入 key,其余线程会失败,避免重复注册。
  2. 使用 Redis 事务 (MULTI / EXEC)

    • MULTI + EXEC 确保多个 Redis 操作在同一事务中执行
    redisTemplate.execute((RedisCallback<Object>) connection -> {
        connection.multi();
        connection.set(key.getBytes(), "registered".getBytes());
        connection.expire(key.getBytes(), 60);
        return connection.exec();
    });
    

    作用

    • 确保写入 key 和设置过期时间是原子的,不会被中途修改。
  3. 使用 Redisson 分布式锁

    • 适用于高并发场景,保证多个服务器之间不会重复注册:
    RLock lock = redissonClient.getLock("register-lock:" + phoneNumber);
    try {
        if (lock.tryLock(5, 10, TimeUnit.SECONDS)) { 
            userService.register(phoneNumber);
        }
    } finally {
        lock.unlock();
    }
    

    作用

    • 只有一个线程能拿到锁,执行注册,其他线程等待或直接返回。

🔍 结论

  • 先查 Redis,若不存在则执行注册并缓存数据,提升性能,减少数据库压力。
  • 但多个 Redis 操作非原子性,可能会导致数据不一致(如并发下重复注册)。
  • 解决方案
    1. SETNX(互斥锁):确保只有一个线程能注册。
    2. Redis 事务:保证多个 Redis 操作同时执行。
    3. Redisson 分布式锁:在多服务器环境下防止并发问题。

最终目标确保并发情况下数据一致性,避免重复注册或异常数据覆盖。 🚀

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值