1、为什么要使用缓存
由于服务器、数据库、网络等资源有限,无法支撑越来越多的请求与计算量,所以将一部分数据放在缓存中,以此减小薄弱环节的计算量和请求流程。
网站中缓存的应用场景:
1:可以缓存整个页面的html,提高访问响应能力;
2:针对局部页面元素进行缓存;
3:对复杂数据的结果进行缓存,例如一个查询需要结合多个数据集,然后根据这些数据集进行相应的运算,即使每个子集查询有缓存,但还是需要额外的运算,这种情况可以考虑缓存计算后的结果。
4:对耗时的查询进行缓存,例如产品列表页的查询。
5:和上下文相关的用户数据,例如用户从订单埴写页进入到订单成功页,或者是从产品列表页点击详细产品进行预订时的订单填写页,此时这两个页面之间都需要传递大量的相关数值,我们可以把所有的数值封装在一个类中,然后通过缓存进行通信。
2、缓存的属性
缓存有以下几个重要属性:
Ø 命中率:命中率指请求次数与正确返回结果次数的比例,越高越好。
影响缓存命中率的因素:
1:数据时实性,每个业务系统都对自己的数据有相应的要求,有些数据的实时性非常强,像每日的股票信息,这种情况如果设置了缓存,缓存的命中率会特别低。
2:缓存粒度问题,一般来说是缓存的跨度太大,即此时的KEY值包含的条件太多,会出现缓存命中率特别低的情况。
提高缓存命中率的方法:
1:增大存储介质的容量;
2:对非常热点的数据进行捕捉,可以采用实时更新缓存的方式来平衡缓存与实时性的问题,例如可以单独开启一个后台服务来定时做更新缓存的工作。
3:调整缓存KEY值的算法,尽量保证缓存KEY的细粒度,KEY-VALUE就是很好的细粒度例子。
4:根据业务调整缓存的过期策略。
Ø 最大元素:缓存中可以存放的元素的最大数量。
Ø 清空策略。清空策略通常有以下几种:
n FIFO:最先进入缓存得数据在缓存空间不够情况下(超出最大元素限制时)会被首先清理出去
n LFU:一直以来最少被使用的元素会被被清理掉。这就要求缓存的元素有一个hit 属性,在缓存空间不够得情况下,hit 值最小的将会被清出缓存。
n LRU:最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
Ø 预热策略
全量预热:一开始就加载全部数据,适用于不怎么变化的数据,比如地区数据
增量预热:查询不到时,从数据源取出放入缓存内。
3、需要注意的问题
3.1缓存穿透
什么是缓存穿透?
一般的缓存系统,都是按照key去缓存查询,如果不存在对应的value,就应该去后端系统查找(比如DB)。如果key对应的value是一定不存在的,并且对该key并发请求量很大,就会对后端系统造成很大的压力。这就叫做缓存穿透。
如何避免?
1:对查询结果为空的情况也进行缓存,缓存时间设置短一点,或者该key对应的数据insert了之后清理缓存。
2:对一定不存在的key进行过滤。可以把所有的可能存在的key放到一个大的Bitmap中,查询时通过该bitmap过滤。【感觉应该用的不多吧】
缓存雪崩
3.2缓存雪崩
当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,也会给后端系统(比如DB)带来很大压力。
如何避免?
1:在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
2:不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。
3:做二级缓存,A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期(此点为补充)
3.3分布式缓存系统
分布式缓存系统面临的问题
3.3.1缓存一致性问题
1:缓存系统与底层数据的一致性。这点在底层系统是“可读可写”时,写得尤为重要
2:有继承关系的缓存之间的一致性。为了尽量提高缓存命中率,缓存也是分层:全局缓存,二级缓存。他们是存在继承关系的。全局缓存可以有二级缓存来组成。
3:多个缓存副本之间的一致性。为了保证系统的高可用性,缓存系统背后往往会接两套存储系统(如memcache,redis等)
3.3.2缓存穿透和缓存雪崩
上面有讲述。
3.3.3缓存数据的淘汰
缓存淘汰的策略有两种: (1) 定时去清理过期的缓存。(2)当有用户请求过来时,再判断这个请求所用到的缓存是否过期,过期的话就去底层系统得到新数据并更新缓存。
两者各有优劣,第一种的缺点是维护大量缓存的key是比较麻烦的,第二种的缺点就是每次用户请求过来都要判断缓存失效,逻辑相对比较复杂,具体用哪种方案,大家可以根据自己的应用场景来权衡。
1. 预估失效时间 2. 版本号(必须单调递增,时间戳是最好的选择)3. 提供手动清理缓存的接口。