互联网大厂是如何设计和使用缓存的?方案已开源!( 二 )


四、混合型缓存设计总体来说,在设计秒杀系统的缓存过程中,会采用 本地缓存+分布式缓存的混合型缓存 设计方案 。其中,本地缓存指的就是单机缓存,比如JVM内存缓存,单机Cache缓存 。分布式缓存指的是以分布式的方式集中管理的缓存,比如Memcached、Redis等,如图3所示 。

互联网大厂是如何设计和使用缓存的?方案已开源!

文章插图
图片
4.1 抗流量洪峰良好的缓存设计不仅仅能够提升系统的总体性能,还能作为抗瞬时流量洪峰的有效防线 。
可以这么说,如果整个秒杀系统前置的流量管控、流量清洗和限流等是秒杀系统流量洪峰的第一道防线,则本地缓存就是抗流量洪峰的第二道防线,而分布式缓存就是第三道防线,如图4所示 。
互联网大厂是如何设计和使用缓存的?方案已开源!

文章插图
图片
使用缓存能够抗一定的流量洪峰,经过前置的流量管控、流量清洗和限流等措施的第一道防线、本地缓存的第二道防线、分布式缓存的第三道防线,真正进入数据库的流量就会比较小了 。
4.2 缓存集群方案从缓存集群模式的角度去分析,每台服务器甚至JVM实例都会拥有自己独立的本地缓存,在承载大并发流量时, , 以本地缓存为主,分布式缓存次之 , 如图5所示 。
互联网大厂是如何设计和使用缓存的?方案已开源!

文章插图
图片
可以看到 , 从缓存的集群模式角度来看 , 每台服务器都会自己独立本地缓存,除了前置的流程管控、流量清洗和限流等措施构筑的流量洪峰第一道防线外 。本地缓存会承接剩余的大部分流量,构筑成流量洪峰的第二道防线,而分布式缓存则是流量洪峰的第三道防线 。
并且在缓存的设计上 , 分布式缓存的作用主要是协调和同步最新数据到本地缓存 。
也就是说,只有本地缓存失效时,才会访问分布式缓存,将分布式缓存中的数据更新到本地缓存中,并且同一时刻只能有一个线程对本地缓存进行更新操作 , 以避免多个线程并发更新本地缓存 。
同样的 , 如果分布式缓存失效,则同一时刻只能有一个线程访问数据库来获取对应的数据,并将其更新到分布式缓存 。
在集群模式下,我们应该尽最大努力将流量拦截在本地缓存,避免过多的请求访问分布式缓存,提高秒杀系统的性能,并且降低秒杀系统由于大量的远程IO导致的各种风险 。
4.3 缓存交互流程采用本地缓存+分布式缓存的混合型缓存架构设计方案时 , 在读取缓存数据时,会优先读取本地缓存的数据,如果本地缓存未开启,或者已经失效 , 此时就会使用分布式缓存 。
也就是说,优先读取本地缓存中的数据,如果本地缓存未开启或者缓存数据失效,则读取分布式缓存中的数据,如图6所示 。
互联网大厂是如何设计和使用缓存的?方案已开源!

文章插图
图片
可以看到,只有在本地缓存未开启或者缓存失效的情况下 , 才会去访问分布式缓存,读取分布式缓存中的数据,并且在同一个时刻只能有一个线程更新本地缓存中的数据,这种方式可以最大限度减少远程IO为秒杀系统带来的风险 。具体的流程如下所示 。
(1)判断本地缓存是否开启,如果开启则进行第2步,否则进行第4步 。
(2)判断本地缓存是否失效,如果未失效 , 则进行第3步,否则进行第4步 。
(3)读取本地缓存数据,读取缓存流程结束 。
(4)判断分布式缓存是否开启,如果开启则进行第5步,否则进行第7步 。
(5)判断分布式缓存是否失效,如果未失效 , 则进行第6步,否则进行第7步 。
(6)读取分布式缓存数据,同一时刻只有一个线程更新本地缓存数据,读取缓存流程结束 。
(7)读取数据库数据 , 同一时刻只有一个线程更新分布式缓存数据,读取缓存流程结束 。
这里,有一个设计技巧需要大家注意:如果本地缓存失效,并且某个线程没有获取到更新本地缓存的机会 , 这个线程需要立即返回而不是在原地阻塞等待,这种方式可以最大限度的节省服务器资源和线程切换的成本 , 尤其是像在秒杀系统这种承接瞬时高并发流量的系统中,这种设计能够节省不少服务器资源 。
这种线程未获取到更新数据的机会而快速返回的机制 , 需要客户端配合在适配处理 , 也就是说,客户端对这种情况需要进行静默处理,不要提示错误信息,也不做其他处理,稍后重新调用接口进行重试即可 。


推荐阅读