来自书籍:Redis开发与运维
Redis作用
redis作为缓存,主要优点有两点:
- 加速读写:缓存是在内存中,从内存中读取速度很快的,比直接从数据库如mysql中拿数据快得多。
- 降低后端负载:减少后端访问量和复杂计算,比如执行很复杂的sql语句。
带来的问题:
- 数据不一致:有一定时间窗口缓存内数据和存储层数据库中数据不一致。
- 增加开发和维护成本:缓存需要增加处理的逻辑代码。后期运维成本也增加,不止要管数据库,还需要管缓存。
缓存更新策略
为了解决缓存中数据和数据库中数据的不一致,制定策略进行同步。
内存中的数据可以通过TTL指令获取其状态:XX :具有时效性的数据; -1 :永不过期的数据;-2 :已经过期或被删除或未定义的数据
- 主动清理策略(LRU/LFU/FIFO算法剔除)
Redis使用maxmemory-policy配置清理策略:
volatile-lru:只对设置了过期时间的key进行LRU(默认值)
allkeys-lru : 删除LRU算法的key
volatile-random:随机删除即将过期key
volatile-ttl : 删除即将过期的key
allkeys-random:随机删除key
noeviction : 永不过期,返回错误
一致性:一致性差,删除哪些key是算法决定,不是开发人员决定。
- 超时删除
定时删除:给缓存数据设置过期时间,过期后自动删除,如expire命令。
惰性删除:当下次读写这个数据时,如果过期了删掉,否则直接用。(类似内存泄漏,即有些key就读一次,过期后再也不用了,这个key就一直不会删除,内存中会有大量应该被删除的数据)
定期删除:周期性轮询缓存中的时效性数据,采用随机抽取的策略,利用过期数据占比的方式控制数据删除。
一致性:一段时间内存在一致性问题。
- 主动更新
数据库中真实数据更新后,立即更新缓存。例如可以利用消息系统或其他方式通知缓存更新。
一致性:一致性最高。但假如主动更新有问题,那么很可能很长时间数据不会更新,应结合超时删除使用。
缓存穿透
查询一个根本不存在的数据,redis不会命中,转向请求存储层,存储层也不会命中,从存储层查不到数据不会写到redis缓存中,那么每次这样的请求来,相当于都绕过redis去存储层查询,失去了缓存存在的意义。
排查:在程序中分别统计总调用树,缓存层命中数,存储层命中数。如果大量存储层空命中,可能是出现了缓存穿透问题。
解决方案:
- 缓存空对象
缓存空对象注意:
a)需要更多内存存这些空值,针对这类数据应该设置一个较短的过期时间,以便于超时自动删除。
b)这段未过期的时间内,如果数据库中更新了这个数据,那么这段时间数据会不一致,可以使用消息通知等主动告知缓存删除这个空数据。 - 使用布隆过滤器
适用于数据命中不高,数据相对固定,实时性低(通常是数据集较大)的应用场景,但是缓存空间占用少。布隆过滤器中没有的数据,说明这个数据一定没有或不存在。 - 分析用户行为,如果是恶意攻击等限制用户的访问权限。
- 修改代码逻辑,直接代码中过滤掉这些无效的空值。
无底洞问题
单机批量操作只需要一次网络交互,分布式批量操作设计多次网络交互,这个时间会增加,导致添加大量新节点后,性能反而下降。
用一句通俗的话总结就是, 更多的节点不代表更高的性能, 所谓“无底
洞”就是说投入越多不一定产出越多。 但是分布式又是不可以避免的, 因为
访问量和数据量越来越大, 一个节点根本抗不住, 所以如何高效地在分布式
缓存中批量操作是一个难点。
缓存雪崩
缓存层由于某种原因不能提供服务(如某瞬间大量key值同时过期失效),所有的请求就打到数据库上,数据库可能崩溃。还有一种自然雪崩场景,即redis崩了,服务器宕机了。
解决方案:
- 保证缓存层服务高可用性。
- 隔离组件,限流并降级措施。如springcloud里Hystrix。
- 把key的过期时间加上一个随机数,让key们均匀的过期,而不是瞬间全部或者大量过期。
缓存击穿
当一个热点key并发量非常大时,缓存失效的瞬间,大量线程会来重建缓存,会向数据库读取这个数据,造成后端负载加大甚至崩溃。重建缓存不能在短时间内完成,可以是一个复杂计算,例如复杂sql,多个IO,多个依赖等。
解决方案:
-
使用互斥锁,让一个线程重建缓存,其他线程等这个线程建好后直接从缓存拿即可。
-
设置热点数据永不过期。为热点数据设计一个逻辑上的过期时间,当超过这个时间时单独开一个线程更新缓存。
这样做可以看出,在开线程更新缓存期间,会出现数据不一致情况。
总结
- 缓存能加快数据读写,降低存储层的负载,缓解存储层压力。但是会带来数据不一致问题。
- 缓存更新策略:应主动清理、超时、主动更新三种方案结合完成。
- 缓存穿透的现象及解决方案
- 无底洞问题线程及解决方案
- 缓存雪崩的现象及解决方案
- 缓存击穿的现象及解决方案