11-1 缓存的收益与成本
收益
- 降低后端负载:
- 对高消耗的SQL结果进行缓存。
- 加速请求响应。
- 将大量写合并为批量写。
- 如计数器先Redis累加在批量写DB。
11-2 缓存的更新策略
- 达到最大内存时删除(maxmemory-policy)。
- 过期时删除。
- 由应用层自己控制。
11-3 缓存粒度控制
11-4 缓存穿透问题
什么是缓存穿透?
缓存穿透的概念很简单,用户想要查询一个数据,发现redis内存数据库没有,也就是缓存没有命中,于是向持久层数据库查询。发现也没有,于是本次查询失败。当用户很多的时候,缓存都没有命中,于是都去请求了持久层数据库。这会给持久层数据库造成很大的压力。这就是缓存穿透。
这里需要注意和缓存击穿的区别,缓存击穿,是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。
产生原因
- 需要更多的键。在面对恶意攻击时,查询参数可能是随机生成的无意义的数值。
方案2:布隆过滤器拦截
布隆过滤器可用于判断一个元素是否在一个超大的集合中存在。对于缓存穿透的场景,先将所有合法的键放入布隆过滤器,然后当有请求到达时,先用布隆过滤器判断要请求的键是否真实存在,如果存在,才进行后续查询。如果不存在,则直接返回miss。布隆过滤器可视为一个大型HashSet。Redis本身没有提供布隆过滤器,但是可以在应用层实现布隆过滤逻辑,然后底层存储用Redis的Bitmap(String)。

对于布隆过滤器,一个比较麻烦的问题是怎么生成这样一个布隆过滤器。如果针对的是固定数据,那么比较简单,只需要初始化时生成一次。但是对于频繁更新的数据,那么实时更新布隆过滤器就比较麻烦。
11-5 缓存雪崩问题
什么是缓存雪崩?
在高并发下,大量的缓存key在同一时间失效(可能是由于key过期,也可能是由于Redis宕机),导致大量的请求落到数据库上,进而造成级联故障。如活动系统里面同时进行着非常多的活动,但是在某个时间点所有的活动缓存全部过期。
注意对比缓存雪崩和缓存击穿。缓存雪崩是指几乎所有缓存都失效,而缓存击穿是指热点数据单条失效。
优化方案
- 保证Redis服务本身高可用,避免发生单点故障。
- 限流降级。这个解决方案的思想是,在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。(以部分请求的响应延长或失败,来换取整个数据层的安全)。
- 数据预热。数据加热的含义就是在正式部署之前,我先把可能的数据先预先访问一遍,这样部分可能大量访问的数据就会加载到缓存中。在即将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。
- 缓存过期时间设置为滑动的。
- 提前演练:压力测试。
了解Hystrix
11-6 无底洞问题
什么是无底洞问题?
11-7 热点key的重建问题(缓存击穿问题)
问题描述
当某个热点key还不存在缓存时,同一时间突然到达很多请求。每一个请求都会发现缓存不存在,然后访问底层数据库,然后重建缓存。这就会造成底层数据库压力,并且响应时间也很慢。
解决方案
(1)互斥锁
对查询数据库、冲减缓冲,这个步骤加互斥锁(写锁,排它锁),即同一时间只允许一个线程执行重建操作。这里的互斥锁可以用redis来实现。
(2)永远不过期
这实际上是保证缓存不被击穿的一种方法。
当第一次设置key的缓存让,让该缓存永不过期,这样就不会出现“缓存击穿”的问题。然后,在应用层判断该缓存是否过期。如果是,就让一个专门的线程去异步更新这个缓存。
这种方案存在的问题是,在数据过期之后,缓存依然会在一段时间内有效,从而造成一定的同步性问题。

