
缓存设计可谓老生常谈了早些时候都是采用memcache现在大家更多倾向使用redis除了知晓常用的数据存储类型结合业务场景有针对性选择好像其他也没有什么大的难点。工程中引入Redis Client二方包初始化一个Bean实例RedisTemplate一切搞定so easy。如果是几十、几百并发的业务场景缓存设计可能并不需要考虑那么多但如果是亿级的系统呢首先先了解缓存知识图谱早期的缓存用于加速CPU数据交换的RAM。随着互联网的快速发展缓存的应用更加宽泛用于数据高速交换的存储介质都称之为缓存。使用缓存时我们要关注哪些指标缓存有哪些应用模式以及缓存设计时有哪些Tip技巧一图胜千言如下七大经典问题缓存在使用过程不可避免会遇到一些问题对于高频的问题我们大概归为了7类。具体内容下面我们一一道来1、缓存集中失效当业务系统查询数据时首先会查询缓存如果缓存中数据不存在然后查询DB再将数据预热到Cache中并返回。缓存的性能比 DB 高 50~100 倍以上。很多业务场景如秒杀商品、微博热搜排行、或者一些活动数据都是通过跑任务方式将DB数据批量、集中预热到缓存中缓存数据有着近乎相同的过期时间。当过这批数据过期时会一起过期此时对这批数据的所有请求都会出现缓存失效从而将压力转嫁到DBDB的请求量激增压力变大响应开始变慢。那么有没有解呢当然有了。我们可以从缓存的过期时间入口将原来的固定过期时间调整为过期时间基础时间随机时间让缓存慢慢过期避免瞬间全部过期对DB产生过大压力。2、缓存穿透不是所有的请求都能查到数据不论是从缓存中还是DB中。假如黑客攻击了一个论坛用了一堆肉鸡访问一个不存的帖子id。按照常规思路每次都会先查缓存缓存中没有接着又查DB同样也没有此时不会预热到Cache中导致每次查询都会cache miss。由于DB的吞吐性能较差会严重影响系统的性能甚至影响正常用户的访问。解决方案方案一查存DB 时如果数据不存在预热一个特殊空值到缓存中。这样后续查询都会命中缓存但是要对特殊值解析处理。方案二构造一个BloomFilter过滤器初始化全量数据当接到请求时在BloomFilter中判断这个key是否存在如果不存在直接返回即可无需再查询缓存和DB3、缓存雪崩缓存雪崩是指部分缓存节点不可用进而导致整个缓存体系甚至服务系统不可用的情况。分布式缓存设计一般选择一致性Hash当有部分节点异常时采用rehash策略即把异常节点请求平均分散到其他缓存节点。但是当较大的流量洪峰到来时如果大流量 key 比较集中正好在某 12 个缓存节点很容易将这些缓存节点的内存、网卡过载缓存节点异常 Crash然后这些异常节点下线这些大流量 key 请求又被 rehash 到其他缓存节点进而导致其他缓存节点也被过载 Crash缓存异常持续扩散最终导致整个缓存体系异常无法对外提供服务。解决方案方案一增加实时监控及时预警。通过机器替换、各种故障自动转移策略快速恢复缓存对外的服务能力方案二缓存增加多个副本当缓存异常时再读取其他缓存副本。为了保证副本的可用性尽量将多个缓存副本部署在不同机架上降低风险。4、缓存热点对于突发事件大量用户同时去访问热点信息这个突发热点信息所在的缓存节点就很容易出现过载和卡顿现象甚至 Crash我们称之为缓存热点。这个在新浪微博经常遇到某大V明星出轨、结婚、离婚瞬间引发数百千万的吃瓜群众围观访问同一个key流量集中打在一个缓存节点机器很容易打爆网卡、带宽、CPU的上限最终导致缓存不可用。解决方案首先能先找到这个热key来比如通过Spark实时流分析及时发现新的热点key。将集中化流量打散避免一个缓存节点过载。由于只有一个key我们可以在key的后面拼上有序编号比如key#01、key#02。。。key#10多个副本这些加工后的key位于多个缓存节点上。每次请求时客户端随机访问一个即可可以设计一个缓存服务治理管理后台实时监控缓存的SLA并打通分布式配置中心对于一些hot key可以快速、动态扩容。5、缓存大Key当访问缓存时如果key对应的value过大读写、加载很容易超时容易引发网络拥堵。另外缓存的字段较多时每个字段的变更都会引发缓存数据的变更频繁的读写导致慢查询。如果大key过期被缓存淘汰失效预热数据要花费较多的时间也会导致慢查询。所以我们在设计缓存的时候要注意缓存的粒度既不能过大如果过大很容易导致网络拥堵也不能过小如果太小查询频率会很高每次请求都要查询多次。解决方案方案一设置一个阈值当value的长度超过阈值时对内容启动压缩降低kv的大小方案二评估大key所占的比例由于很多框架采用池化技术如Memcache可以预先分配大对象空间。真正业务请求时直接拿来即用。方案三颗粒划分将大key拆分为多个小key独立维护成本会降低不少方案四大key要设置合理的过期时间尽量不淘汰那些大key6、缓存数据一致性缓存是用来加速的一般不会持久化储存。所以一份数据通常会存在DB和缓存中由此会带来一个问题如何保证这两者的数据一致性。另外缓存热点问题会引入多个副本备份也可能会发生不一致现象。解决方案方案一当缓存更新失败后进行重试如果重试失败将失败的key写入MQ消息队列通过异步任务补偿缓存保证数据的一致性。方案二设置一个较短的过期时间通过自修复的方式在缓存过期后缓存重新加载最新的数据7、数据并发竞争预热互联网系统典型的特点就是流量大一旦缓存中的数据过期、或因某些原因被删除等导致缓存中的数据为空大量的并发线程请求查询同一个key就会一起并发查询数据库数据库的压力陡然增加。如果请求量非常大全部压在数据库可能把数据库压垮进而导致整个系统的服务不可用。解决方案方案一引入一把全局锁当缓存未命中时先尝试获取全局锁如果拿到锁才有资格去查询DB并将数据预热到缓存中。虽然client端发起的请求非常多但是由于拿不到锁只能处于等待状态当缓存中的数据预热成功后再从缓存中获取为了便于理解简单画了个流程图。这里面特别注意一个点由于有一个并发时间差所以会有一个二次check缓存是否有值的校验防止缓存预热重复覆盖。方案二缓存数据创建多个备份当一个过期失效后可以访问其他备份。写在最后缓存设计时有很多技巧优化手段也是千变万化但是我们要抓住核心要素。那就是让访问尽量命中缓存同时保持数据的一致性。