使用缓存的正确姿式

缓存是如今系统中必不可少的模块,而且已经成为了高并发高性能架构的一个关键组件。这篇博客咱们来分析一下使用缓存的正确姿式。sql

缓存能解决的问题

  • 提高性能数据库

    绝大多数状况下,select 是出现性能问题最大的地方。一方面,select 会有不少像 join、group、order、like 等这样丰富的语义,而这些语义是很是耗性能的;另外一方面,大多数应用都是读多写少,因此加重了慢查询的问题。缓存

    分布式系统中远程调用也会耗不少性能,由于有网络开销,会致使总体的响应时间降低。为了挽救这样的性能开销,在业务容许的状况(不须要太实时的数据)下,使用缓存是很是必要的事情。网络

  • 缓解数据库压力架构

    当用户请求增多时,数据库的压力将大大增长,经过缓存可以大大下降数据库的压力。​并发

缓存的适用场景

  • 对于数据实时性要求不高异步

    对于一些常常访问可是不多改变的数据,读明显多于写,适用缓存就颇有必要。好比一些网站配置项。分布式

  • 对于性能要求高ide

    好比一些秒杀活动场景。​高并发

缓存三种模式

通常来讲,缓存有如下三种模式:

  • Cache Aside 更新模式

  • Read/Write Through 更新模式

  • Write Behind Caching 更新模式

通俗一点来说就是,同时更新缓存和数据库(Cache Aside 更新模式);先更新缓存,缓存负责同步更新数据库(Read/Write Through 更新模式);先更新缓存,缓存定时异步更新数据库(Write Behind Caching 更新模式)。这三种模式各有优劣,能够根据业务场景选择使用。

Cache Aside 更新模式

这是最经常使用的缓存模式了,具体的流程是:

  • 失效:应用程序先从 cache 取数据,没有获得,则从数据库中取数据,成功后,放到缓存中。
  • 命中:应用程序从 cache 中取数据,取到后返回。
  • 更新:先把数据存到数据库中,成功后,再让缓存失效

mark
           Cache Aside 更新模式流程图

注意咱们上面所提到的,缓存更新时先更新数据库,而后在让缓存失效。那么为何不是直接更新缓存呢?这里有一些缓存更新的坑,咱们须要避免入坑。

避坑指南一

先更新数据库,再更新缓存。这种作法最大的问题就是两个并发的写操做致使脏数据。以下图(以Redis和Mysql为例),两个并发更新操做,数据库先更新的反然后更新缓存,数据库后更新的反而先更新缓存。这样就会形成数据库和缓存中的数据不一致,应用程序中读取的都是脏数据。

mark

避坑指南二

先删除缓存,再更新数据库。这个逻辑是错误的,由于两个并发的读和写操做致使脏数据。以下图(以Redis和Mysql为例)。假设更新操做先删除了缓存,此时正好有一个并发的读操做,没有命中缓存后从数据库中取出老数据而且更新回缓存,这个时候更新操做也完成了数据库更新。此时,数据库和缓存中的数据不一致,应用程序中读取的都是原来的数据(脏数据)。

mark

避坑指南三

先更新数据库,再删除缓存。这种作法其实不能算是坑,在实际的系统中也推荐使用这种方式。可是这种方式理论上仍是可能存在问题。以下图(以Redis和Mysql为例),查询操做没有命中缓存,而后查询出数据库的老数据。此时有一个并发的更新操做,更新操做在读操做以后更新了数据库中的数据而且删除了缓存中的数据。然而读操做将从数据库中读取出的老数据更新回了缓存。这样就会形成数据库和缓存中的数据不一致,应用程序中读取的都是原来的数据(脏数据)。

mark

可是,仔细想想,这种并发的几率极低。由于这个条件须要发生在读缓存时缓存失效,并且有一个并发的写操做。实际上数据库的写操做会比读操做慢得多,并且还要加锁,而读操做必需在写操做前进入数据库操做,又要晚于写操做更新缓存,全部这些条件都具有的几率并不大。可是为了不这种极端状况形成脏数据所产生的影响,咱们仍是要为缓存设置过时时间。


Read/Write Through 更新模式

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

mark

                  Read/Write Through 更新模式流程图


Read Through

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

Write Through

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

Write Behind Caching 更新模式

Write Behind Caching 更新模式就是在更新数据的时候,只更新缓存,不更新数据库,而咱们的缓存会异步地批量更新数据库。这个设计的好处就是直接操做内存速度快。由于异步,Write Behind Caching 更新模式还能够合并对同一个数据的屡次操做到数据库,因此性能的提升是至关可观的。

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

mark
                           Write Behind Caching 更新模式流程图


总结

三种缓存模式的优缺点:

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

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

                          

                                                      -----END-----


         喜欢本文的朋友们,欢迎扫一扫下图关注公众号撸码那些事,收看更多精彩内容

                                       

相关文章
相关标签/搜索