美文网首页
缓存更新策略 - 一致性

缓存更新策略 - 一致性

作者: GOGOYAO | 来源:发表于2019-07-17 09:27 被阅读0次

[TOC]

参考

Cache Aside Pattern

究竟先操作缓存,还是数据库?

缓存更新的套路

使用缓存的正确姿势

缓存的正确使用方式,你都会了吗?

1. 概要

缓存服务和数据服务(如数据库)是独立的系统,更新的时候,无法做到原子性地操作两个服务,即有两步操作来更新缓存服务和数据服务的数据。因此在并发读写以及第二步操作异常时,会出现各种问题。

此处说的第二步操作,要么是操作缓存服务,要么是操作数据服务,根据具体的实现方式而定。

2. 缓存使用的误区

  1. 服务与服务之间不要通过缓存传递数据

  2. 如果缓存挂掉,可能导致雪崩,此时要做高可用缓存,或者水平切分

  3. 调用方不宜再单独使用缓存存储服务底层的数据,容易出现数据不一致,以及反向依赖

  4. 不同服务,缓存实例要做垂直拆分

3. 常见更新模式

3.1. cache aside

同时更新缓存和数据库
这是最常用的pattern了。其具体逻辑如下:

  • 数据查询:应用程序先从cache取数据,没有得到,则从数据库中取数据,成功后,放到缓存中。
  • 数据更新:先把数据存到数据库中,成功后,再让缓存失效。
    此处,更新的时候是先数据库,后缓存,也有另一种思路,先缓存,后数据库。两种思路详见后文分析。

3.1.1. 先数据库后缓存

这是最常用最常用的pattern。
分析异常情况如下:

  • \color{blue}{并发读写}
    • 一个是查询请求,一个是更新请求的并发。更新请求在写完数据库之后,让缓存失效之前,查询请求到来,此时,缓存依然有效,所以,并发的查询请求拿的是没有更新的数据,但是,随后的更新请求会马上让缓存的失效了,在这之后的查询请求再把数据从数据库中取出来,放入到缓存。\color{green}{不会出现不一致。}
    • 同样是一个查询请求,一个更新请求的并发。查询请求 cache miss,读了数据库,在将数据放到缓存之前,更新请求到来,更新了数据库内容且让缓存失效,此时查询请求再将之前读的老数据放入到缓存。这样也会导致缓存和数据不一致。\color{red}{出现不一致,只是概率很小。} 为了避免这种情况,可以采取延迟失效,同时读请求回填 cache 的时候,如果遇到 key 存在,则不更新,只能等待超时失效之后,读请求回填的 cache 才可以更新;也可以考虑延迟双删,缓存失效后,等待1s 再失效一次缓存。
  • \color{red}{第二步操作异常}:如果是更新请求在写完数据库,让缓存失效之前,更新请求发起方异常(如宕机),此时数据库已经更新了,但是缓存一直没有更新。导致查询请求从缓存获取的数据一直都是老数据。为了规避第二步操作异常,有两种方法:1. 考虑在缓存上加上失效时间,但是这和周期性的更新缓存类似,还是会对系统性能还是有较大的影响;2. 使用可靠的消息队列记录(如 binlog)更改,然后消费消息进行缓存失效

这是标准的design pattern,包括Facebook的论文《Scaling Memcache at Facebook》也使用了这个策略。

3.1.2. 先缓存后数据库

分析如下:

  • \color{red}{并发读写}:一个是查询请求,一个是更新请求的并发。在更新请求让缓存失效之后,写数据库之前,查询操作到来,此时,会从数据库读到老数据进而写回到缓存,等更新请求写完数据库,\color{red}{就会出现不一致。}

  • \color{red}{第二步操作异常}:如果是更新请求将缓存失效后,在写数据库前,更新操作发起方异常(如宕机),此时仅仅是 cache miss,\color{green}{不会导致不一致。}

针对第二种方案,我们可以采取以下两种方案规避并发读写的问题:

  1. 在使缓存失效的时候,不是立即失效,而是采取延迟失效(比如说设置缓存失效时间为1s),保证在数据库更新之前,读请求依旧能够命中cache,进而不会出现读请求更新 cache 的情况。这个方案应该是最佳方案。
  2. 延迟双删。第一次缓存失效且数据库操作完成之后,延迟再失效一次缓存。

3.1.3. 更新缓存还是失效缓存

为什么不是更新缓存,而是失效缓存?你可以看一下Quora上的这个问答《Why does Facebook use delete to remove the key-value pair in Memcached instead of updating the Memcached during write request to the backend?》,主要是\color{red}{怕两个并发的更新操作导致脏数据.}

3.1.3. 结论

个人觉得,先缓存后数据库,配合延迟失效的方案最好。欢迎读者讨论。

3.2. Read/Write Through Pattern

先更新缓存,缓存负责同步更新数据库。

在上面的 Cache Aside 更新模式中,应用代码需要维护两个数据存储,一个是缓存(Cache),一个是数据库(Repository)。而在Read/Write Through 更新模式中,应用程序只需要维护缓存,数据库的维护工作由缓存代理了。


write/read through流程图

3.2.1. Read Through

Read Through 模式就是在查询操作中更新缓存,也就是说,当缓存失效的时候,Cache Aside 模式是由调用方负责把数据加载入缓存,而 Read Through 则用缓存服务自己来加载。

3.2.2. Write Through

Write Through 模式和 Read Through 相仿,不过是在更新数据时发生。当有数据更新的时候,如果没有命中缓存,直接更新数据库,然后返回。如果命中了缓存,则更新缓存,然后由缓存自己更新数据库(这是一个同步操作)。

3.3. Write Behind Caching Pattern

先更新缓存,缓存定时异步更新数据库。

Write Behind Caching 更新模式就是在更新数据的时候,只更新缓存,不更新数据库,而我们的缓存会异步地批量更新数据库。这个设计的好处就是直接操作内存速度快。因为异步,Write Behind Caching 更新模式还可以合并对同一个数据的多次操作到数据库,所以性能的提高是相当可观的。

但其带来的问题是,数据不是强一致性的,而且可能会丢失。另外,Write Behind Caching 更新模式实现逻辑比较复杂,因为它需要确认有哪些数据是被更新了的,哪些数据需要刷到持久层上。只有在缓存需要失效的时候,才会把它真正持久起来。

write behind 流程图

3.4. 总结

三种缓存模式的优缺点:

  • Cache Aside 更新模式实现起来比较简单,但是需要维护两个数据存储,一个是缓存(Cache),一个是数据库(Repository)。
  • Read/Write Through 更新模式只需要维护一个数据存储(缓存),但是实现起来要复杂一些。
  • Write Behind Caching 更新模式和Read/Write Through 更新模式类似,区别是Write Behind Caching 更新模式的数据持久化操作是异步的,但是Read/Write Through 更新模式的数据持久化操作是同步的。优点是直接操作内存速度快,多次操作可以合并持久化到数据库。缺点是数据可能会丢失,例如系统断电等。

缓存是通过牺牲强一致性来提高性能的。所以使用缓存提升性能,就是会有数据更新的延迟。这需要我们在设计时结合业务仔细思考是否适合用缓存。然后缓存一定要设置过期时间,这个时间太短太长都不好,太短的话请求可能会比较多的落到数据库上,这也意味着失去了缓存的优势。太长的话缓存中的脏数据会使系统长时间处于一个延迟的状态,而且系统中长时间没有人访问的数据一直存在内存中不过期,浪费内存。

相关文章

  • Redis学习--缓存设计

    缓存的收益和成本 加速读写 降低后端负载缓存层+存储层基本流程 数据不一致性 代码维护成本 运维成本 缓存更新策略...

  • 缓存问题

    一、缓存更新策略 一般情况来说,缓存更新策略有三种: 先删除缓存,后更新数据库 先更新数据库,后更新缓存 先更新数...

  • OkHttp3(十二)--CacheInterceptor

    CacheInterceptor 用来负责读取缓存以及更新缓存的 读取候选缓存 创建缓存策略 根据缓存策略决定报错...

  • SpringCache优化、缓存一致性、多级缓存

    先记录一些纲要 1、SpringCache是写库之后更新的策略,对缓存一致性的不太友好 2、继承RedisCach...

  • 缓存的一致性

    最终一致性 给缓存设置过期时间,是保证最终一致性的解决方案。 场景:更新缓存呢,还是删除缓存? Cache Asi...

  • 缓存一致性解决方案

    1.实时同步更新 特点:更新数据库的同时,更新缓存,使用缓存工具类或者AOP实现 优点:数据一致性强,不会出现缓存...

  • 聊聊缓存一致性

    相信大家都听过缓存一致性,随便百度一下就有各种文章,无非就是更新数据库和缓存的先后顺序及策略。一般有3种方案:先更...

  • 3种方案保证数据库与缓存的一致性

    3种方案保证数据库与缓存的一致性 延时双删策略删除缓存重试机制读取biglog异步删除缓存

  • 高并发架构修炼

    redis缓存策略 分布式缓存一致性 redis常见的问题 redis分布式锁 http://ifeve.com/...

  • 缓存更新策略 - 一致性

    [TOC] 参考 Cache Aside Pattern 究竟先操作缓存,还是数据库? 缓存更新的套路 使用缓存的...

网友评论

      本文标题:缓存更新策略 - 一致性

      本文链接:https://www.haomeiwen.com/subject/gtgtlctx.html