缓存能够有效地加速应用的读写速度,同时也可以降低后端负载,对日常应用的开发至关重要。下面会介绍缓存使用技巧和设计方案,包含如下内容:缓存的收益和成本分析、缓存更新策略的选择和使用场景、缓存粒度控制方法、穿透问题优化、无底洞问题优化、雪崩问题优化、热点key重建优化。
1)缓存的收益和成本分析
下图左侧为客户端直接调用存储层的架构,右侧为比较典型的缓存层+存储层架构。
下面分析一下缓存加入后带来的收益和成本。
收益:①加速读写:因为缓存通常都是全内存的,而存储层通常读写性能不够强悍(例如MySQL),通过缓存的使用可以有效地加速读写,优化用户体验。
②降低后端负载:帮助后端减少访问量和复杂计算(例如很复杂的SQL语句),在很大程度降低了后端的负载。
成本:①数据不一致性:缓存层和存储层的数据存在着一定时间窗口的不一致性,时间窗口跟更新策略有关。
②代码维护成本:加入缓存后,需要同时处理缓存层和存储层的逻辑,增大了开发者维护代码的成本。
③运维成本:以Redis Cluster为例,加入后无形中增加了运维成本。
缓存的使用场景基本包含如下两种:
①开销大的复杂计算:以MySQL为例子,一些复杂的操作或者计算(例如大量联表操作、一些分组计算),如果不加缓存,不但无法满足高并发量,同时也会给MySQL带来巨大的负担。②加速请求响应:即使查询单条后端数据足够快(例如select*from tablewhere id=),那么依然可以使用缓存,以Redis为例子,每秒可以完成数万次读写,并且提供的批量操作可以优化整个IO链的响应时间。
2)缓存更新策略
缓存中的数据会和数据源中的真实数据有一段时间窗口的不一致,需要利用某些策略进行更新,下面会介绍几种主要的缓存更新策略。
①LRU/LFU/FIFO算法剔除:剔除算法通常用于缓存使用量超过了预设的最大值时候,如何对现有的数据进行剔除。例如Redis使用maxmemory-policy这个配置作为内存最大值后对于数据的剔除策略。
②超时剔除:通过给缓存数据设置过期时间,让其在过期时间后自动删除,例如Redis提供的expire命令。如果业务可以容忍一段时间内,缓存层数据和存储层数据不一致,那么可以为其设置过期时间。在数据过期后,再从真实数据源获取数据,重新放到缓存并设置过期时间。例如一个视频的描述信息,可以容忍几分钟内数据不一致,但是涉及交易方面的业务,后果可想而知。
③主动更新:应用方对于数据的一致性要求高,需要在真实数据更新后,立即更新缓存数据。例如可以利用消息系统或者其他方式通知缓存更新。
三种常见更新策略的对比:
有两个建议:①低一致性业务建议配置最大内存和淘汰策略的方式使用。
②高一致性业务可以结合使用超时剔除和主动更新,这样即使主动更新出了问题,也能保证数据过期时间后删除脏数据。
3)缓存粒度控制
缓存粒度问题是一个