缓存穿透
黑客故意去请求缓存中不存在的数据(如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询),导致所有的请求都怼到数据库上,从而数据库连接异常
解决方案
(一) 利用互斥锁,缓存失效的时候,先去获得锁,得到锁了,再去请求数据库。没得到锁,则休眠一段时间重试
(二) 采用异步更新策略,无论 key 是否取到值,都直接返回。value 值中维护一个缓存失效时间,缓存如果过期,异步起一个线程去读数据库,更新缓存。需要做缓存预热操作
(三) 采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的 bitmap 中,一个一定不存在的数据会被这个 bitmap 拦截掉,从而避免了对底层存储系统的查询压力。另外也有一个更为简单粗暴的方法:如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟(浪费内存,且可能对业务有影响)
缓存雪崩
缓存同一时间大面积的失效,这个时候又来了一波请求,结果请求都怼到数据库上,从而导致数据库连接异常
解决方案
(一) 给缓存的失效时间,加上一个随机值,避免集体失效
(二) 用加锁或者队列的方式控制读数据库写缓存的线程数量,比如对某个 key 只允许一个线程查询数据和写缓存,其他线程等待,但是该方案吞吐量明显下降了
(三) 双缓存。设置两个缓存,缓存 A 和缓存 B。缓存 A 的失效时间为 20 分钟,缓存 B 不设失效时间。自己做缓存预热操作:
- 从缓存 A 读数据库,有则直接返回
- A 没有数据,直接从 B 读数据,直接返回,并且异步启动一个更新线程
- 更新线程同时更新缓存 A 和缓存 B
缓存击穿
对于一些设置了过期时间的 key,如果这些 key 可能会在某些时间点被超高并发地访问,是一种非常 “热点” 的数据。这个时候,需要缓存被 “击穿” 的问题,这个和缓存雪崩的区别在于这里针对某一 key 缓存,前者则是很多 key
解决方案
(一) 利用互斥锁,缓存失效的时候,先去获得锁,得到锁了,再去请求数据库。没得到锁,则休眠一段时间重试(有可能存在死锁风险:如果另外的线程获取的资源正好是当前线程锁定的资源,同时当前线程即将要去访问另外线程正锁定的内容,就出现了死锁)
(二) 提前使用互斥锁
在 value 内部设置 1 个超时值 timeout1,timeout1 比实际的超时时间 timeout2 小。当从 cache 读取到 timeout1 发现它已经过期时,马上延长 timeout1 并重新设置到cache。然后再从数据库加载数据并设置到 cache 中
(三) 分级缓存
网友评论