redis缓存一致性问题

缓存一致性问题中的三大读写策略?

补充,

  1. 因为db的innodb中使用的mvcc锁机制下来保证线程安全,而mvcc并不是强同步性,其中对读操作是可以允许一定的并发的,而db的读操作会对缓存实行写操作,导致缓存可能会被并发更新,而导致出现db和缓存数据不一致的情况发生。 --解决方案:对缓存的更新实行锁/分布式锁来解决这个并发更新带来的线程安全问题。
  2. 所以,应用层对缓存的更新可能是并发的db对缓存的更新同样也可能是并发的

1. 旁路缓存模式(cache aside Pattern)

  1. 写:先更新DB;然后删除缓存 redis缓存一致性问题_第1张图片

  2. 读:先读cache,cache中存在则直接返回;ceche不存在,则读db;将读取到的内容同步到cecheredis缓存一致性问题_第2张图片

  3. 问题1:在写缓存的时候,我们可以先删除cache,再更新db嘛? ----> 不可以,可能出现缓存不一致的问题,比如请求1先删除变量A;接着请求2读取变量A,此时就会到db中读取,并又更新到缓存中;再接着变量A接着执行更新db,此时db中是最新数据,而缓存中是旧数据,缓存不一致。

  4. 问题2:在写缓存的时候。先更新db,后删除cache就没有什么问题吗? —> 不一定,不过概率很小。请求1读取数据A;接着请求2更新数据A,并删除缓存中的数据A;然后,请求1把读取到的旧数据写到缓存中,导致db新数据,缓存旧数据,(发生的场景是在,请求1缓存穿透到db中读取数据A时,正好请求2开始更新db中的数据A,innodb中的mvcc的锁机制并不是强同步的,允许一定的并发,这就导致可能出现问题);还有就是删除缓存可能会失败,比如此时缓存服务器暂时不可用,解决方法 —>缓存失效时间变短(指标不治本),增加cache的更新失败重试机制(常用),就是如果更新缓存失败,就隔一段时重新尝试更新,重试次数可以自己定。如果多次尝试还是失败则会将删除失败操作的key加入对列中,等缓存服务器可用之后再删除key。----> 解决办法:面对数据库和缓存的强一致性的情况,可以使用锁/分布式锁来保证更新缓存的时候的不存在线程安全问题;面对数据库和缓存的可以允许短暂非一致性的情况,更新db时候同步更新cache,并且设置过期时间,保证数据不一致的情况的影响在可控范围。

  5. 缺陷1:首次请求数据一定不在缓存的问题 – >提前往cache中加入热数据。

  6. 缺陷2:写操作频繁的话,会导致cache中的数据更新操作也会频繁被删除,这样会影响命中率

2. 读写穿透(read/write through pattern)

  1. 写: 先查cache中是否存在,不存在则直接更新db;存在,则更新cache的值;然后cache再将值同步到db中redis缓存一致性问题_第3张图片

  2. 读:先查cache,是否存在,存在直接读取返回;不存在,到db中读取,并返回到cache中redis缓存一致性问题_第4张图片

  3. 特点:读写缓存在db中读取数据时,并刷回cache时,是cache服务器自己完成的,对用户透明;旁路缓存模式,在db中读取时,刷回缓存这一步,是用户自己负责完成的。

  4. 缺陷1:首次读取数据不再缓存的情况

3. 异步缓存写入 (write behind pattern)

  1. 异步缓存写入与缓存穿透类似,不同之处在于:写操作时,更新完cache后,并不是立刻马上将数据刷到db,而是等一段时候后统一刷回。 ----->总结:Read/Write Through 是同步更新 cache 和 DB,而 Write Behind Caching 则是只更新缓存,不直接更新 DB,而是改为异步批量的方式来更新 DB。
  2. 缺点:很明显,在服务器出现崩溃,重启的时候,会丢失在服务器中还没刷回db的数据
  3. 优点:这种模式下的db的写性能很高,非常适合一些数据经常发生变化的情况,比如点赞量,浏览量等、

4. 缓存一致性的过程中遇到的两大问题和解决方案(基于上面的三大读写策略来分析)?

  1. 操作失败问题:在上面三大读写策略过程中可能遇到,更新数据库失败或删除缓存失败
    - 解决办法: 重试(失败后重新尝试更新)
    - 不断重试:因为刚操作失败,立即尝试可能会造成再次失败;并且不断尝试会消耗性能。
    - 异步重试:基于不断尝试的弊端,我们提出来了利用消息对列或订阅变更日志(binLog)的方式来实现异步重试;(订阅变更日志binlog,以为对数据库的更新操作都会在binlog中有记录,我们redis的订阅binlog就可以知道db执行的有哪些变更操作)
  2. 并发带来的不一致问题:这个在上面已经有了详细说明,与强一致性时的解决方案和不强一致性时的解决方案:这里想说的是在 读写分离+缓存时怎么尽量保证一定的一致性。
    - 解决方案:延迟双删策略
    - 并发不一致现象:
    线程 A 更新主库 数据A
    线程 A 删除缓存 数据A
    线程 B 查询缓存数据A,没有命中,查询「从库」得到数据A的旧值
    从库「同步」完成
    线程 B 将 数据A的「旧值」写入缓存
    最后 完成主从一致后,我们的db数据A是新值,缓存中数据A是旧值
    - 解决的思路:当更新完主库后,并不立即执行删除缓存数据的操作;延迟一段时间后再执行删除缓存操作。 —》理想情况是上诉过程,完成主从同步+线程B的更新缓存的操作后再执行是最好的,但是精确的测量太消耗性能,所以我们只能对这个延迟时间做一个大概的估计值,将不一致的时间维持在一个合理的时间内

你可能感兴趣的:(redis,redis,缓存,数据库)