# redis缓存击穿,缓存穿透,缓存雪崩和缓存刷新
# 什么是缓存?
【1】缓存就是数据交换的缓冲区(称作:Cache),当某一硬件要读取数据时,会首先从缓存汇总查询数据,有则直接执行,不存在时从内存中获取。由于缓存的数据比内存快的多,所以缓存的作用就是帮助硬件更快的运行。
【2】缓存往往使用的是RAM(断电既掉的非永久存储),所以在用完后还是会把文件送到硬盘等存储器中永久存储。电脑中最大缓存就是内存条,硬盘上也有16M或者32M的缓存。
【3】高速缓存是用来协调CPU与主存之间存取速度的差异而设置的。一般CPU工作速度高,但内存的工作速度相对较低,为了解决这个问题,通常使用高速缓存,高速缓存的存取速度介于CPU与主存之间。系统将一些CPU在最近几个时间段经常访问的内容存在高速缓存,这样就在一定程度上缓解了由于主存速度低造成的CPU“停工待料”的情况。
【4】缓存就是把一些外存上的数据保存在内存上而已,为什么保存在内存上,我们运行的所有程序里面的变量都是存放在内存中的,所以如果想将值放入内存上,可以通过变量的方式存储。在JAVA中一些缓存一般都是通过Map集合来实现的。
# 缓存击穿
# 产生原因
程序在处理缓存时,一般是先从缓存查询,如果缓存没有这个key获取为null,则会从DB中查询,并设置到缓存中去。
按这种做法,那查询一个一定不存在的数据值,由于缓存是不命中时需要从数据库查询,查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,造成缓存穿透。
# 解决办法
# 缓存雪崩
# 产生原因
指的是 大量缓存集中在一段时间内失效,发生大量的缓存穿透,所有的查询都落在数据库上,造成了缓存雪崩。
# 解决办法
1.这个没有完美解决办法,但可以分析用户行为,尽量让失效时间点均匀分布,设置不同的过期时间。 2.要么给每个key的过期时间加一个随机值,避免同时过期,达到错峰刷新缓存的目的。 3.用加 ***分布式锁或者分布式队列的方式保证缓存的单线程(进程)写 (eg. redis的 SETNX)***,从而避免失效时大量的并发请求落到底层存储系统上。在加锁方法内先从缓存中再获取一次(防止另外的线程优先获取锁已经写入了缓存),没有再查DB写入缓存。 (当然也可以: 在没有获取锁(tryLock)的线程中一直轮询缓存,至超限时)
# 产生原因
对于 Redis 挂掉了,请求全部走数据库,也属于缓存雪崩
# 解决办法
事发前:实现 Redis 的高可用(主从架构+Sentinel 或者 Redis Cluster),尽可能避免 Redis 挂掉这种情况。
事发中:万一 Redis 真的挂了,我们可以设置+ 限流(hystrix),尽量避免我们的数据库被干掉。
事发后:Redis 持久化,重启后自动从磁盘上加载数据,快速恢复缓存数据。
# 缓存击穿
# 产生原因
指的是热点key在某个特殊的场景时间内恰好失效了,恰好有大量并发请求过来了,造成DB压力。
# 解决办法
1.与缓存雪崩的解决方法类似: 用加锁或者队列的方式保证缓存的单线程(进程)写,在加锁方法内先从缓存中再获取一次,没有再查DB写入缓存。
2.物理上的缓存是不设置超时时间的(或者超时时间比较长), 但是在缓存的对象上增加一个属性来标识超时时间(此时间相对小)。 当获取到数据后,校验数据内部的标记时间,判定是否快超时了,如果是,异步发起一个线程(控制好并发)去主动更新该缓存。
这种方式会导致一定时间内,有些请求获取缓存会拿到过期的值,看业务是否能接受而定。
# 缓存刷新
既清空缓存 ,一般在 Insert、Update、Delete 操作后就需要刷新缓存,如果不执行就会出现脏数据。但当缓存请求的系统蹦掉后,返回给缓存的值为null。
# 相关资料
redis缓存击穿、缓存穿透、缓存雪崩 (opens new window)