前言

缓存要解决的问题是速度的问题,使用缓存的目的是为了减少对物理资源的访问,缓存大量的应用在软硬件的方方面面,从 CPU 到硬盘,就应用了 一级缓存、二级缓存,少部分高速缓存和大量低速缓存相结合,以提高 CPU 的计算能力,本文讲的主要是系统集成项目中的软件级别的缓存。

缓存因果图

缓存在现代系统中的位置可以说是举足轻重,不是可有可无的问题了,而是怎么样用好的问题。怎么样讲好缓存这个话题,我思考了很久;如果是生搬硬套,我估计我肯定是凉凉,但要是上来就一顿代码操作,显得字太多,各位看官老爷也是要喷死我的(你们喷我算我赢)。各位看官就当故事随便看看就好了。

客户端缓存

小明同学是一个大学生,每个月都会从爸爸那里领取生活费,由于爸爸给的生活费比较充裕,他就将一部分存了起来,周而复始,固定频率,这就是浏览器网页缓存;有一天,他接到爸爸的一个电话,说每个月给现金,我总是从银行拿也不方便,这样,咱给存银行卡里头,但是说好了,每个月1号的时候转1000块进去,3号之前可以领取,过时不侯。这就是 Cookie!小明说爸,这时间太紧迫了,我万一太过于专心学习忘记取钱那你儿子可是要饿死的吖,到时候没人给你养老送终事儿就大了。他爸爸经过深思熟虑后,决定放开取现时间的限制:行,你想什么时候取都成,就是别把卡给弄掉了就行。这就是LocalStorage/IndexDB,浏览器本地存储。如果银行卡掉了(浏览器重装,删库跑路),那还得找爸爸。

服务器缓存

我们本次讨论的重点就是服务器缓存,对于小明的爸爸来说,每次给小明生活费之前都要到银行(数据库)去取钱出来,实在是太麻烦,不如提前把钱取出来准备好,每次取半年的钱出来放家里保险箱里(缓存),小明申请生活费的时候,直接给他就好了,节约时间效率又高,唯一的问题就是可能没那么安全,有可能被小偷把钱给偷了(缓存更新),然后就是半年时间过后再重新取一笔出来(缓存过期策略)。

静态对象缓存

静态对象也是一种特殊的缓存,静态对象作用于程序的整个生命周期中。需要注意的是,静态对象不会被 GC 回收 ,但是,如果静态对象被多次引用覆盖,那么之前的引用就有可能被 GC 回收。这就好像,小明在向爸爸领取生活费的时候,发现这次领到手的钱实在是太破旧了,都不好意思花出去,爸爸只好重新给了另一份。

会话级缓存

在 Web 站点中,Session 是私有制的,各个 Session 之间是不会共享内存对象的,我们可以利用这个特性(在Asp.NET 时代常用)来暂时保存一些数据,例如用户购物车。还是以小明的需求为例子,小明下面还有一个妹妹,妹妹每个月都可以从爸爸那里多领取200元生活费,看到妹妹的资源这么好,小明愤愤不平,就像妹妹提议共享生活费,一家人一起用,多好吖!结果小明被爸爸揍了一顿,还收回了部分生活费。

进程级别缓存

基本上每个应用程序都具有本地缓存的能力,在 Asp.NET 中就有 MemoryCache ,也叫做进程级缓存(本地缓存),MemoryCache 和 分布式缓存的作用基本一致,所不同的是,本地缓存在应用程序停止后就会被释放掉,无法进行持久存储。就好像,小明在上大学期间,每个月都是生活费的,但是到暑假的时候就没有了(只能靠苦逼的暑期工挣点生活费啊)。

分布式缓存

分布式缓存是个好东西,目前市场上出现了非常多的 nosql 数据库,都可用作缓存数据库,有时候,这些缓存数据库也提供持久化的能力。

小明家的生活条件,在经过爸爸的不懈努力之后,生活水平渐渐提高了;有一天,爸爸对小明说:明仔吖,咱们家现在生活水平提高了,但是爸爸也越来越忙了,这样,如果我忙的时候,你问妈妈或者爷爷奶奶,也是可以拿到生活费的,咱们家这几位长辈手里都有钱了,这就是分布式缓存。

但是分布式缓存又分为主从模式和集群模式,上面说的是集群模式,爸爸妈妈爷爷奶奶都可以拿钱,但是主从模式就不同了,主从模式就是钱都在爸爸手里,爸爸会把钱分给每个长辈一份,如果当时正好爸爸太忙了,没来得及分给妈妈,而小明又恰好问妈妈要生活费的话;妈妈只能对他说:小命呀,不好意思,妈妈这里也没有,你看看再问问其它人(客户端自己轮询),在问了妈妈、爷爷后(引用指向),终于知道,钱在爸爸那里,还得问爸爸要生活费。而且爸爸给其它人分钱的时候,还要占用他工作的时间。

缓存雪崩

缓存雪崩就是在某一个时刻,大量的缓存同时失效,造成数据库访问压力倍增。小明的爸爸最近压力比较大,因为收入减少了,他爸爸做的一个工程因为种种原因,货款只能分期付清,为了防止小明和妹妹同时申请生活费,造成资金周转困难;爸爸规定妹妹 1 号领取生活费,而小明在 5 号才能领取,小明心里的苦啊!

缓存穿透

缓存穿透就是客户端总是尝试访问某个不存在的缓存,造成了每次都要取请求数据库读取数据。就好像小明吧,本来生活挺平静的,这刚上大二,就交了个女朋友,每个月的 1000 元生活费有点捉襟见肘,然后他又不能让爸爸知道,就在申请生活费的时候,每次都多要一点;这样就搞得爸爸很被动,本来计划得好好的,每个月都是 1000 块,这没次都不够,老是要跑银行取现金,终于在3个月后,爸爸发现了这个问题,想着儿子也大了,为了早日抱上孙子,就提高了小明的生活费,解决了每次都要跑银行的问题。

缓存击穿

缓存击穿和缓存雪崩有点类似,其中不同的是;缓存雪崩是大量缓存 key 同时过期,而缓存击穿是大量的请求指向同一个缓存key,在这个 key 过期的时候,大量的请求涌入数据库中,造成了瞬间巨大的压力。举个栗子,因为小明交了女朋友的原因,他的生活费总是很快用完了;这种情况下,他也必须在 1 号的时候拿到生活费,不然就要吃土了,但是妈妈不允许他们一起取打扰爸爸,妈妈就指定了妹妹去问,在妹妹没有回来之前,小明只能等着,这就是为了解决缓存击穿而采用的策略:互斥锁(mutex key)

运维级别缓存

除了在应用程序中可以接入缓存以外,部分运维工具也集成了缓存服务,比如 Nginx、IIS。

代理缓存

Nginx 就是反向代理缓存,通过配置 Nginx 的缓存功能,在客户端请求到来到时候去加载缓存内容,用以提高响应能力,IIS 缓存又分为用户缓存和内核缓存。IIS 的输出缓存设置中,内核模式缓存不会对验证等用户信息进行检查,就好像小明等爸爸因为太赶时间,把钱放门卫大妈那里了,结果随便来了个学生就把小明等生活费给领走了,但是加上用户模式缓存后,就可以添加对身份的检查(请求标头),这样大妈就会知道谁是小明而不会随便把生活费交给别人了。

网页静态化

这种技术,在 Web1.0 时代非常的流行,我还记得那些个年头的网站开发项目中的要求,大部分项目的需求分析文档里面就明晃晃的写着:网页静态化!,而静态化常见于各种企业型、论坛帖子,在发表这些信息后就将其生成静态网页,客户端访问的时候,直接重定向到该静态网页,基本无需访问数据库。

CDN 缓存

CDN 缓存类似于上面提到的分布式缓存,但是实际上 CDN 缓存服务目前来说,主要说针对静态资源的,比如图片、视频、文件等等;大家经常可以看到,很多站点都号称提供了 CDN 加速服务,这些站点就像一个个代办信用卡的销售中介,实际上拿的都是银行的资源。

更形象一些的说法,就是大家的钱都是中国人民银行发行的,我们可以通过各个不同的银行(CDN节点)查询我们的银行卡余额(我怎么可能有余额),在以前没有手机银行的时候,大家就可以到附近的银行去查询,然后取款(CDN缓存),如果附近的银行的柜员机没有现金,那么可能就需要到总行去取了(回源),如果发生了太多回源的事件,就会造成 CDN 的拥堵,所以 CDN 服务商也不敢打包票自己的基础服务没有问题,反正我是没见到哪家 CDN 服务商敢注明服务稳定性 100%,基本上都是 99.99%

结束语

缓存的理论知识,其实是非常宏大的,我这里只是抛砖引玉,希望能给入门的朋友带来一点帮助,如果你喜欢这篇文章,请给我点赞,让更多同学可以看到。

06-25 14:13