Redis-11-Redis与Mysql的数据一致性

1.是什么

数据一致性呢,讲的就是缓存中的数据和db中的数据是否能一致。

2.为什么

先看我这篇文章,了解下缓存策略:Redis-6-三种缓存读写策略

在文章中,介绍了并发场景下的一致性问题,我们已经确定了一个基本思路:

先更新db,再删除缓存。

这个方案,奠定了我们在读写操作时的基本思路。

2.1 主从复制延迟

在我们Mysql读写分离+主从复制延迟的场景下,缓存重建用了一个错误的值。

我们主库更新完数据并删除缓存后,缓存确实不存在了。

但是,db的从库读数据有稍许延迟,在这个时间段我们读取,缓存没数据,确实触发了缓存重建。

但是由于读取的从库还没有同步到主库更新过来的数据,导致我们重建的缓存值还是从库中的旧数据,仍然会有问题。

2.2 缓存服务宕机

假设我们的缓存服务宕机了,无法删除缓存。

这个直接就跟没删除缓存的操作一样,缓存的完全是个错误的旧数据。

3.解决

3.1 延时双删

针对主从同步未完成的情况,我们简单的方案就是,等一小会再删除。

来一个demo。

我们RedisService中存放操作缓存的方法,没啥特殊的,就是正常的操作缓存。

@Service
public class RedisService {

    @Autowired
    private StringRedisTemplate redisTemplate;

    public String get(String key) {
        return redisTemplate.opsForValue().get(key);
    }

    public void set(String key, String value, long timeout, TimeUnit unit) {
        redisTemplate.opsForValue().set(key, value, timeout, unit);
    }

    public void delete(String key) {
        redisTemplate.delete(key);
    }
}

DataService呢,记录我们完整的操作步骤。

嗯,你可以看到,实际上,我们在首次删除后,小小休息了下,又触发了次 redisService.delete(key);

@Service
public class DataService {

    @Autowired
    private RedisService redisService;

    public void updateData(String key, String value) {
        updateDatabase(key, value);
        // 首次删除
        redisService.delete(key);
        // 延迟双删
        deleteCacheWithDelay(key);
    }
    
    // 模拟数据库操作
    public void updateDatabase(String key, String value) {
    }

    @Async
    public void deleteCacheWithDelay(String key) {
        try {
            TimeUnit.SECONDS.sleep(5); // 延迟5秒
            redisService.delete(key);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

问题是,这个延迟删除缓存,延迟时间到底设置要多久?

  • 延迟时间要大于主从复制的延迟时间。确保删除后,新的读请求重建出来的是正确的数据值。
  • 延迟时间要大于一次完整的缓存重建时间。即大于一个读请求查询db再更新缓存的时间,这样才能确保哪怕真有某个线程重建了个错误的,咱们还是能给它删除掉。

好了,你已经懂延时双删的概念了,现在,请你给出删除时间。

哈哈,懵了吧,我咋知道?嗯,问题就是,我咋知道?

实际情况下,这个时间是很难把控的,咱们就是完全凭借经验来设置个值,尽可能的去保证,极端情况下呢,它还是会有问题。

3.2 消息队列

这种方式,核心上还是一个再次删除的方案。

嗯,不就是延迟删除下缓存嘛,我们当然可以新建个延时消息。

mq消费端,收到消息后,删除对应的缓存。

这就是延时双删的升级版,通过咱们mq的机制,确保肯定能删除掉(消费成功),这样就算缓存宕机or代码报错了也能ok。

那么,还是会存在一个问题,我咋知道时间具体设置多久啊?

还是那句话,很难把控。

3.3 分布式事务

如果要做到强一致性,常见的解决方案就是分布式事务了:吃透此文,分布式事务会被你玩的出神入化

但是,分布式事务的引入,必然又涉及到复杂性的增加,也带来了性能的损失。

那我们引入缓存的初心是什么,不是为了性能吗,所以,这其实是个难以平衡的点。

需要你结合自己的实际场景,权衡使用。

posted @ 2024-06-09 18:43  羊37  阅读(9)  评论(0编辑  收藏  举报