对于缓存而言,redis
- 约束好key的规范,合理肯定Val值的大小,选择合适的缓存介质(相应的根据实际的业务状况分析,哪一种缓存方式合适 redis、zk、memcached 亦或者 local)。(生产实际案例:redis作缓存,因Val值过大的key太多,致使reids的性能出现了问题,形成业务不可用)
- 合理分配缓存失效时间(缓存是物理有失效时间 ? 亦或者 在缓存的对象里增长一个虚拟的失效时间,而物理上是没有过时时长的或比较长的过时时间)
- 缓存没值,是继续轮询获取呢,仍是暂缓本次获取(retry 次数和超时时间的处理)?仍是竞争锁去查db呢?
- 合理加锁控制分布式下全局单线程查询DB写缓存操做
缓存穿透
程序在处理缓存时,通常是先从缓存查询,若是缓存没有这个key获取为null,则会从DB中查询,并设置到缓存中去。数据库
按这种作法,那查询一个必定不存在的数据值,因为缓存是不命中时须要从数据库查询,查不到数据则不写入缓存,这将致使这个不存在的数据每次请求都要到数据库去查询,形成缓存穿透。后端
解决办法
- 最好对于每个缓存key都有必定的规范约束,这样在程序中对不符合parttern的key 的请求能够拒绝。(但通常key都是经过程序自动生成的)
- 将可能出现的缓存key的组合方式的全部数值以hash形式存储在一个很大的bitmap中<布隆过滤器>(须要考虑如何将这个可能出现的数据的hash值以后同步到bitmap中, eg. 后端每次新增一个可能的组合就同步一次,或者 穷举),一个必定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力
- 经常使用: 若是对应在数据库中的数据都不存在,咱们将此key对应的value设置为一个默认的值,好比“NULL”,并设置一个缓存的失效时间。固然这个key的时效比正常的时效要小的多

有点相似于将一个key经过n个不一样的hash函数定位成n个整数,而后将这n个整数定位在一个长度在M的初始数值为0的数组下标上,设置该n个下标的数值为1。 那只要当查询过来,用这n个hash函数定位断定都为1那基本就存在,只要有任一下标的数组值不是1,则表明不存在。数组
缓存雪崩
指的是大量缓存集中在一段时间内失效,发生大量的缓存穿透,全部的查询都落在数据库上,形成了缓存雪崩。缓存
解决办法
- 这个没有完美解决办法,但能够分析用户行为,尽可能让失效时间点均匀分布,设置不一样的过时时间。
- 用加分布式锁或者分布式队列的方式保证缓存的单线程(进程)写 (eg. redis的 SETNX),从而避免失效时大量的并发请求落到底层存储系统上。在加锁方法内先从缓存中再获取一次(防止另外的线程优先获取锁已经写入了缓存),没有再查DB写入缓存。 (固然也能够: 在没有获取锁(tryLock)的线程中一直轮询缓存,至超限时)
缓存击穿
指的是热点key在某个特殊的场景时间内刚好失效了,刚好有大量并发请求过来了,形成DB压力。并发
解决办法
与缓存雪崩的解决方法相似: 用加锁或者队列的方式保证缓存的单线程(进程)写,在加锁方法内先从缓存中再获取一次,没有再查DB写入缓存。 异步
还有一种比较好用的(针对缓存雪崩与缓存击穿):分布式
物理上的缓存是不设置超时时间的(或者超时时间比较长), 可是在缓存的对象上增长一个属性来标识超时时间(此时间相对小)。 当获取到数据后,校验数据内部的标记时间,断定是否快超时了,若是是,异步发起一个线程(控制好并发)去主动更新该缓存。memcached
这种方式会致使必定时间内,有些请求获取缓存会拿到过时的值,看业务是否能接受而定。函数