首页 / m.itcdj.com / 正文

缓存策略落地的一线踩坑笔记

上周三晚上十点,我还在公司机房蹲服务器。

为啥?

上周做的618大促活动,商品详情页接口平时响应120ms,那天直接飙到3s,运维大哥拍着告警屏幕骂,说我们组的缓存策略写得像筛子。我当时盯着红得发亮的监控曲线,连手里攥的冰可乐都化了半罐。

最先踩的不是命中率的坑

之前团队做缓存优化,所有人的KPI都盯着缓存命中率,非要把指标提到99%才算合格。那天我们拉了监控数据算,命中率足足有98.7%,离目标差一点,还是直接把库打挂了。

哪是什么缓存不够。

是所有商品详情的缓存过期时间设得一模一样。为了图省事,当初写代码的时候,所有key的TTL全设的30分钟,算下来大促开场的时间点,刚好是上线后第30分钟——所有缓存集体到期,那1.3%的未命中请求,全挤在第一分钟砸到数据库上,数据库连接池直接被打满,后续所有请求全堵在半路上。

说出来好笑,就这么个几行代码就能搞定的随机偏移改动,之前团队开了三次技术评审会,居然没人提过。所有人都在算怎么把命中率再提0.2个百分点,没人想过集体过期的风险。

缓存雪崩请求流量峰值监控图

后来我们给所有固定TTL的缓存都加了随机偏移,原本30分钟的过期时间,改成25到35分钟之间随机取值,哪怕多存5分钟的冗余数据,也比集体崩盘强。改完之后再压测,哪怕10倍流量打过来,数据库的QPS也稳稳压在安全线以下,连运维大哥都挑不出毛病。

别把缓存当免费的万能补丁

做技术久了总能遇到这种团队:接口慢了就加缓存,不管什么数据都往Redis里塞,觉得只要加了缓存,性能问题就解决了。

上次我们组接用户积分接口,负责开发的同学上来就把全量用户的积分数据缓存到Redis,TTL设了整整1小时。结果上线第二天,客诉直接爆了300多单——用户刚充完值涨了积分,过半小时再查,还是之前的旧数。

我那天接客诉电话接到嗓子哑,真的想把当初写这段缓存逻辑的同学拉过来闻闻线上告警的味道。

不是说缓存不能用,是你得先搞清楚手里的数据是什么性质。像商品详情介绍、活动规则说明这类,改一次可能半个月都不动的数据,缓存设长点完全没问题;但涉及到用户实时资产的,比如积分、余额、实时库存,你要是敢光靠过期时间保证一致性,迟早要出大问题。

之前还有个更离谱的操作,把实时商品库存也做了全量缓存,大促的时候页面显示还有100件库存,实际早就卖空了,用户付完款才发现发不了货,那体验有多糟,做过电商的都懂。

缓存一致性双写流程示意图
缓存一致性双写流程示意图

我们后来给这类强一致要求的数据定了死规矩:写操作必须先更新数据库,再主动删除对应的缓存key,绝对不能先写缓存等过期。哪怕多花几毫秒的删除操作,也比让用户看到错的数据强。

真正好用的策略从来不是抄来的

说实话,我之前也犯过教条主义的错。刚看完几本架构经典,就把Cache Aside、Read Through、Write Behind这些模式背得滚瓜烂熟,觉得随便拿一个出来就能解决所有问题。

比如Write Behind模式,听着就很高大上——写请求直接落到缓存,异步批量刷数据库,性能直接拉满。但这个模式根本不适合核心交易链路啊。你要是把订单支付的链路改成Write Behind,哪天Redis挂了,缓存里的订单没来得及刷到数据库,用户付了钱订单没生成,那赔的钱够扣掉整个团队半年的绩效。

当时我刚提完这个方案,就被技术总监按住了。他说你先去把过去半年因为缓存出的17次线上故障列出来,再谈改架构的事。我列完才发现,80%的故障都不是因为策略不够先进,是我们把教科书上的模式硬套到了根本不匹配的业务场景上。

现在我们做缓存策略,根本不追求什么教科书级的完美设计。就先算两个数:接口要求扛多少QPS,数据库最多能承受多少峰值,在中间找最稳妥的平衡点。比如热点商品的详情页,我们直接把数据永久存在本地内存里,加个主动更新的通道,平时根本不打数据库,也就不用天天担心雪崩的问题;对于文章浏览量这类非核心统计数据,就用异步批量刷库的模式,哪怕数据延迟个两三分钟,用户也根本感知不到,还能把数据库的压力降下来。

上周复盘的时候,运维大哥买了三杯冰可乐过来,说现在的监控曲线看着就舒服,再也不用大半夜爬起来接告警了。

其实哪有什么高深的缓存策略啊。不就是别把书里的东西硬套到自己业务上,多想想线上真实会出什么问题,少追求那些虚头巴脑的指标。