项目中的缓存那些事儿

1.为什么须要缓存?

在高并发请求时,为什么咱们频繁提到缓存技术?最直接的缘由是,目前磁盘IO和网络IO相对于内存IO的成百上千倍的性能劣势。web

作个简单计算,若是咱们须要某个数据,该数据从数据库磁盘读出来须要0.1s,从交换机传过来须要0.05s,那么每一个请求完成最少0.15s(固然,事实上磁盘和网络IO也没有这么慢,这里只是举例),该数据库服务器每秒只能响应67个请求;而若是该数据存在于本机内存里,读出来只须要10us,那么每秒钟可以响应100,000个请求。redis

经过将高频使用的数据存在离cpu更近的位置,以减小数据传输时间,从而提升处理效率,这就是缓存的意义。数据库

2.在哪里用缓存?

一切地方。例如:浏览器

咱们从硬盘读数据的时候,其实操做系统还额外把附近的数据都读到了内存里,例如,CPU在从内存里读数据的时候,也额外读了许多数据到各级cache里,各个输入输出之间用buffer保存一批数据统一发送和接受,而不是一个byte一个byte的处理,上面这是系统层面,在软件系统设计层面,不少地方也用了缓存:缓存

浏览器会缓存页面的元素,这样在重复访问网页时,就避开了要从互联网上下载数据(例如大图片) web服务会把静态的东西提早部署在CDN上,这也是一种缓存服务器

数据库会缓存查询,因此同一条查询第二次就是要比第一次快 内存数据库(如redis)选择把大量数据存在内存而非硬盘里,这能够看做是一个大型缓存,只是把整个数据库缓存了起来网络

应用程序把最近几回计算的结果放在本地内存里,若是下次到来的请求仍是原请求,就跳过计算直接返回结果并发

3.常见事故分析

一些常见的事故,系统又是怎么设计的呢?app

通常底层是数据库,中间放了一层redis,前面的业务系统所需的数据都直接从redis里取,而后计算出结果返回给app;数据库和redis的同步另外有程序保证,避免redis的穿透,防止了程序里出现大量请求从redis里找不到,因而又一窝蜂的去查数据库,直接压垮数据库的状况。从这个角度讲,其实这一步是作的还能够的。分布式

可是这个系统有两个问题:

1.业务系统须要的数据虽然都在redis里,可是是分开存放的。什么意思呢,好比我前台发起一个请求,后台先去redis里取一下标题,而后再取一下做者,而后再取一下内容,再取一下评论,再取一下转发数等等……结果前台一次请求,后台要请求redis十几回。高并发的时候,压力一下被放大十几倍,redis响应、网络响应必然会变慢。

2.其实作业务的那波人也意识到了这个状况可能发生,因此作了熔断机制,另起了一个缓存池,里面放了一些备用数据,若是主业务超时,直接从缓存池里取数据返回。可是他们设计的时候没想周全,这个备选池的数据过时时间设计的太长了,里面竟然还有好多天前更新进去的数据,最终致使了一大波用户刷出来好久前的不可描述小视频……

说到这,不知道读者有没有意识到他们最致命的一个问题:这个业务系统彻底没有考虑本地缓存(也就是在业务服务器内存里作缓存)。好比像咱们这种app,一旦大量用户同一时间涌进来,一定都是奔着少数几个内容去的,这种特别集中的高频次极少许数据访问,又不须要对每一个用户作特化的,简直就是在脸上写上“请缓存我”。

这时候,若是能在业务端作一层本地缓存,直接把算好的数据本地存一份,那么就会极大减小网络和redis的压力,不至于当场触发熔断了。

4.浅谈缓存的那些坑

缓存颇有用,可是缓存用很差也会埋不少坑:

缓存穿透

缓存穿透是说收到了一个请求,可是该请求缓存里没有,只能去数据库里查询,而后放进缓存。这里面有两个风险,一个是同时有好多请求访问同一个数据,而后业务系统把这些请求全发到了数据库;第二个是有人恶意构造一个逻辑上不存在的数据,而后大量发送这个请求,这样每次请求都会被发送到数据库,可能致使数据挂掉。

怎么应对这种状况呢?对于恶意访问,一个思路是事先作校验,对恶意数据直接过滤掉,不要发到数据库层;第二个思路是缓存空结果,就是对查询不存在的数据仍然记录一条该数据不存在在缓存里,这样能有效的减小查询数据库的次数。

那么非恶意访问呢?这个要结合缓存击穿来说。

缓存击穿

上面提到的某个数据没有,而后好多请求都被发到数据库其实能够归为缓存击穿的范畴:对于热点数据,当数据失效的一瞬间,全部请求都被下放到数据库去请求更新缓存,数据库被压垮。

怎么防范这种问题呢?一个思路是全局锁,就是全部访问某个数据的请求都共享一个锁,得到锁的那个才有资格去访问数据库,其余线程必须等待。可是如今的业务都是分布式的,本地锁无法控制其余服务器也等待,因此要用到全局锁,好比用redis的setnx实现全局锁。

另外一个思路是对即将过时的数据主动刷新,作法能够有不少,好比起一个线程轮询数据,好比把全部数据划分为不一样的缓存区间,按期分区间刷新数据等等。这第二个思路又和咱们接下来要讲的缓存雪崩有关系。

缓存雪崩

缓存雪崩是指好比咱们给全部的数据设置了一样的过时时间,而后在某一个历史性时刻,整个缓存的数据所有过时了,而后瞬间全部的请求都被打到了数据库,数据库就崩了。

解决思路要么是分治,划分更小的缓存区间,按区间过时;要么是给每一个key的过时时间加个随机值,避免同时过时,达到错峰刷新缓存的目的。

缓存刷新

说到刷新缓存,其实也有坑的。好比我以前的一份工做里,有一次大活动,正是如火如荼的时候,全部的广告位忽然都变空白了。后来追查缘由,全部的广告素材都在缓存里,而后起了个程序,专门负责刷新缓存,每次把当前的素材全量刷新。

坏就坏在这个全量上。由于大活动的时候流量极大,广告更新压力也很大,把负责提供更新素材的程序压崩了。刷新缓存的程序在请求时,收到了一个返回结果Null。接下来就喜闻乐见了,刷新程序根据这个null,清空了整个缓存,全部广告素材都失效了。

总之,想要作好高并发系统的缓存,就要考虑到各类边角状况,当心设计,任何细小的疏忽均可能致使系统崩溃。

更多干货和资料请直接联系我,也能够加群710520381,邀请码:柳猫,欢迎你们共同讨论

相关文章
相关标签/搜索