【线上故障分析】深入理解缓存预热

缓存不预热会怎么样?我帮大家淌了路。缓存不预热会导致系统接口性能下降,数据库压力增加,更重要的是导致我写了两天的复盘文档,在复盘会上被骂出了翔。

悲惨的上线时刻

事情发生在几年前,我刚毕业时,第一次使用缓存内心很激动。需求场景是虚拟商品页面需要向用户透出库存状态,提单时也需要校验库存状态是否可售卖。但是由于库存状态的计算包含较复杂的业务逻辑,耗时比较高,在500ms以上。如果要在商品页面透出库存状态那么商品页面耗时增加500ms,这几乎是无法忍受的事情。

如何实现呢?最合适的方案当然是缓存了,我当时设计的方案是如果缓存有库存状态直接读缓存,如果缓存查不到,则计算库存状态,然后加载进缓存,同时设定过期时间。何时写库存呢? 答案是过期后,cache miss时重新加载进缓存。 由于计算逻辑较复杂,库存扣减等用户写操作没有同步更新缓存,但是产品认可库存状态可以有几分钟的状态不一致。为什么呢?

因为仓库有冗余库存,就算库存状态不一致导致超卖,也能容忍。同时库存不足以后,需要运营补充库存,而补充库存的时间是肯定比较长的。虽然补充库存完成几分钟后,才变为可售卖的,产品也能接受。 梳理完缓存的读写方案,我就沉浸于学习Redis的过程。

第一次使用缓存,我把时间和精力都放在Redis存储结构,Redis命令,Redis为什么那么快等方面的关注。如饥似渴的学习Redis知识。

直到上线阶段我也没有意识到系统设计的缺陷。

代码写的很快,测试验证也没有问题。然而上线过程中,就开始噼里啪啦的报警,开始我并没有想到报警这事和我有关。直到有人问我,"XXX,你是不是在上线库存状态的需求?"。

我人麻了,"怎么了,啥事",我颤抖的问

"商品页面耗时暴涨,赶紧回滚"。一个声音传来

"我草",那一瞬间,我的血压上涌,手心发痒,心跳加速,头皮发麻,颤抖的手不知道怎么在发布系统点回滚,"我没回滚过啊,咋回滚啊?"

"有降级开关吗"? 一个声音传来。

"没写..."。我回答的时候觉得自己真是二笔,为啥没加降级啊。(这也是复盘被骂的重要原因)

那么如何对缓存进行预热呢?

如何预热缓存

灰度放量

灰度放量实际上并不是缓存预热的办法,但是确实能避免缓存雪崩的问题。例如这个需求场景中,如果我没有放开全量数据,而是选择放量1%的流量。这样系统的性能不会有较大的下降,并且逐步放量到100%。

虽然这个过程中,没有主动同步数据到缓存,但是通过控制放量的节奏,保证了初始化缓存过程中,不会出现较大的耗时波动。

例如新上线的缓存逻辑,可以考虑逐渐灰度放量。

扫描数据库刷缓存

如果缓存维度是商品维度或者用户维度,可以考虑扫描数据库,提前预热部分数据到缓存中。

开发成本较高。除了开发缓存部分的代码,还需要开发扫描全表的任务。为了控制缓存刷新的进度,还需要使用线程池增加并发,使用限流器限制并发。这个方案的开发成本较高。

通过数据平台刷缓存

这是比较好的方式,具体怎么实现呢?

数据平台如果支持将数据库离线数据同步到Hive,Hive数据同步到Kafka,我们就可以编写Hive SQL,建立ETL任务。把业务需要被刷新的数据同步到Kafka中,再消费Kafka,把数据写入到缓存中。在这个过程中通过数据平台控制并发度,通过Kafka 分片和消费线程并发度控制 缓存写入的速率。

这个方案开发逻辑包括ETL 任务,消费Kafka写入缓存。这两部分的开发工作量不大。并且相比扫描全表任务,ETL可以编写更加复杂的SQL,修改后立即上线,无需自己控制并发、控制限流。在多个方面ETL刷缓存效率更高。

但是这个方案需要公司级别支持 多个存储系统之间可以进行数据同步。例如mysql、kafka、hive等。

除了首次上线,是否还有其他场景需要预热缓存呢?

需要预热缓存的其他场景

如果Redis挂了,数据怎么办

刚才提到上线前,一定要进行缓存预热。还有一个场景:假设Redis挂了,怎么办?全量的缓存数据都没有了,全部请求同时打到数据库,怎么办。

除了首次上线需要预热缓存,实际上如果缓存数据丢失后,也需要预热缓存。所以预热缓存的任务一定要开发的,一方面是上线前预热缓存,同时也是为了保证缓存挂掉后,也能重新预热缓存。

假如有大量数据冷启动怎么办

假如促销场景,例如春节抢红包,平时非活跃用户会在某个时间点大量打开App,这也会导致大量cache miss,进而导致雪崩。 此时就需要提前预热缓存了。具体的办法,可以考虑使用ETL任务。离线加载大量数据到Mafka,然后再同步到缓存。

总结

  1. 一定要预热缓存,不然线上接口性能和数据库真的扛不住。
  2. 可以通过灰度放量,扫描全表、ETL数据同步等方式预热缓存
  3. Redis挂了,大量用户冷启动的促销场景等场景都需要提前预热缓存。
相关推荐
Python私教4 小时前
model中能定义字段声明不存储到数据库吗
数据库·oracle
Estar.Lee5 小时前
查手机号归属地免费API接口教程
android·网络·后端·网络协议·tcp/ip·oneapi
BestandW1shEs7 小时前
谈谈Mysql的常见基础问题
数据库·mysql
重生之Java开发工程师7 小时前
MySQL中的CAST类型转换函数
数据库·sql·mysql
教练、我想打篮球7 小时前
66 mysql 的 表自增长锁
数据库·mysql
Ljw...7 小时前
表的操作(MySQL)
数据库·mysql·表的操作
哥谭居民00017 小时前
MySQL的权限管理机制--授权表
数据库
2401_857610037 小时前
SpringBoot社团管理:安全与维护
spring boot·后端·安全
wqq_9922502777 小时前
ssm旅游推荐系统的设计与开发
数据库·旅游