redis高级篇 缓存双写一致性之更新策略

闲聊

缓存通用查询3部曲

  1. redis 中数据,返回redis 中的数据
  2. redis 中没有,查询数据库并返回
  3. 完成第二部的同时,将数据库查询结果写到redis,redis和数据库数据一致.

谈谈双写一致性的理解

1.如果redis 中有数据:需要和数据库中的相同
2.如果redis 中无数据: 数据库中的值如果是最新的,则要写入到redis

redis 缓存种类

  1. 只读(通过命令的方式写入,不是由我们的java程序,不常用)
  2. 读写(常用)

redis 读写缓存的策略

  1. 同步直写策略
    写数据后,也同步写到redis,缓存和数据库中的数据一致。
    对于读写缓存来说,要想保证缓存和数据库中的数据一致,就要采用同步直写策略
  2. 异步缓存策略
    正常业务运行中,一定时间后才将数据写到redis(比如下单后的快递信息)
    异常情况出现后,需要将失败的动作进行修补,有可能需要借助mq等消息队列。

问题

1. 如果使用redis,那就回涉及到redis 缓存与数据库双写问题。是先动redis呢,还是先动mysql?

首先我们的目的是明确的:就是保证redis和mysql的一致性。

给缓存设置过期时间,定期清理缓存并会写,是最终保证一致性的解决方案。
我们可以对缓存数据设置过期时间,所有的写以数据库为准,对缓存操作尽最大
努力即可。也就是说数据库写入成功,而缓存写入失败,那么只要达到过期时间,
则后面的请求会从数据库中读取新值回写缓存。从而达到最终一致性。

在了解一下4中更新策略

  1. 先更新数据库,在更新缓存
    如果更新mqsql 成功,redis 失败,则数据不一致
    缓存中使错误的数据,数据不一致,且高并发下问题更是严重
    redis高级篇 缓存双写一致性之更新策略_第1张图片

  2. 线程缓存,在更新数据库
    如果redis 成功,mqsql失败,则数据不一致

redis高级篇 缓存双写一致性之更新策略_第2张图片
3. 先删除缓存,在更新数据库
如果redis 删除成功, 数据库删除失败,则数据不一致
下面有问题的代码
redis高级篇 缓存双写一致性之更新策略_第3张图片

A线程执行下面的代码,删除缓存后,正在更新数据库,
线程B要获取这个缓存 ,没有查到,线程B就把数据回写到缓存,
然后线程A更新完数据库后,发现缓存还在
导致缓存中一直都是错的数据。

处理办法1 延迟双删除
这个方案在第一次删除之后,延迟一段时间再次进行删除,所以我们把它叫做“延迟双栓”
redis高级篇 缓存双写一致性之更新策略_第4张图片

  1. 先更新数据库,在删除缓存 (可用,也有问题,但是比上面的3种更好)
    如果mqsql成功,redis 失败,只要是mysql 是正确的,下次通过回写到reids,保证一致性
    ali的cannel也是这个思想, myssql有个中间件canel,可以完成biglog日志订阅功能。 先更新数据库,在更新缓存。
    redis高级篇 缓存双写一致性之更新策略_第5张图片
如果上述的问题也不能容忍(在通过在get一次,以取得正确的数据),那我们只能借助mq与mysql的binlog日志了
![在这里插入图片描述](https://img-blog.csdnimg.cn/355cb7ab3eab4bc6a9122eea84ab8d82.png)

1.我们可以监听 binlog 日志, 但我们知道那些key 和那些表的关系的时。我们便可以筛选出我们要监听的sql语句了。
2.当监听到 执行删除缓存关联的sql语句时,便把这个key放到消息队列。
3.通过mq 中消息执行删除操作。删除成功则已出,删除失败重试。如果重试次数较多,加入死信队列,后续处理,排查故障。

但是这样做有一定的延迟性。比如充值话费,高峰期;1分钟后到账。

2.了解延迟双删吗

延迟双删,
是指在第一次删除之后,延迟一段时间再次进行删除,所以我们把它叫做“延迟双栓”。 目的是 在第一次删除和第二次删除之前,防止有别的线程将值回写到reids.

线程A sleep的时间,需要大于线程B读取数据在写入缓存的时间。
这个时间该怎么确定呢?
第一种方法:
在业务运行的时候统计下线程读取数据与写回数据的时间 T1。结合自己业务,只要保证双删之间的时间 大于T1百毫秒即可。

第二种方法:
新启动后台监控程序,后面将会提到 watchDog监控程序。

这种同步删除降低吞吐量怎么办?
因为要删除2次,所以吞吐量会降低,另起一个线程,异步删除。
redis高级篇 缓存双写一致性之更新策略_第6张图片

3. 微服务查询redis无,mysql有。为了保证双写一致性,会写redis 时需要注意什么?,了解双检加锁吗,如何避免缓存击穿?

一般情况下,使用上面的缓存三部曲即可,但如果是高并发就不行了。想想看,
统一时刻有1000个请求过来,代码都执行 判断redis中有无数据,也就是要
1000次,接着因为redis中没有,又是1000次访问数据库,再接着又是1000次
的会写redis. 这个在高并发下,明显是不可取的。
所有出现 双检加锁。

那什么是双检加锁呢?

 public String doubleCheckCache() {
        // 先从redis 中查询
        String key = "user:1000";
        String s = stringRedisTemplate.opsForValue().get(key);
        if (!StringUtils.hasLength(s)) {
            // 加锁,防止qps高时,同时查到redis中无数据的情况下,继续访问数据库。防止缓存击穿。
            synchronized (this) {
                // 再次检查redis,第一个进入同步的线程,已经将数据写回了redis
                s = stringRedisTemplate.opsForValue().get(key);
                if (!StringUtils.hasLength(s)) {
                    //模拟查询数据库
                    s = "我是小明";
                    // 会写redis
                    stringRedisTemplate.opsForValue().setIfAbsent(key,s);
                    // 这里要考虑数据库如果为空的时候,要怀疑这ke能时恶意的,续作我们将其加入黑明单。
                }
            }
        }
        return s;
    }

5. redis 和mysql 100%会出纰漏,做不到强一致性,如何保证最终一致性?

消息队列,有延迟,
延迟双删除,线程A 时间不好估算
等待删除后,执行查询命令,不支持高并发

你可能感兴趣的:(Redis,redis)