上周六晚上十一点半,我刚把泡好的泡面放到桌上,微信就弹出来十几条@我的消息。是做电商的老周发的。他们当天的大促压测直接崩了。一开始所有人都以为是数据库扛不住,扒了半小时监控才发现,所有请求全打在了主库上——缓存集群的命中率只有12%。合着忙了半个月上的缓存,等于白做。
别上来就抄大厂的缓存配置
老周他们之前在技术分享会上抄了某头部电商的LRU缓存配置,觉得人家用着好使,自己直接照搬就行。结果呢?人家的缓存集群是跨三地部署的,单节点内存是256G,热key的预热脚本跑了快一年,哪些商品会爆单早就摸得门清。老周他们的缓存集群是单机房的,单节点内存才32G,预热脚本?根本没写。大促压测的时候,前10个爆单的商品刚把缓存占满,剩下的冷门商品数据一进来,直接把之前的热key全踢出去了。用户点进来一看,全是未命中,转头就去查数据库,主库的CPU瞬间飙到100%,直接挂了。我当时看到监控截图,主库的CPU曲线直接是一条直线顶在100%,红得晃眼。

说实话,我见过太多团队犯这种错。觉得大厂的方案就是银弹,不管自己的业务规模、硬件配置、流量模型,直接照搬。人家的缓存是给亿级用户用的,你总共日活才十万,学人家搞多级缓存?学人家搞热点key拆分?纯纯白费力气。
你以为的缓存过期,可能根本没生效

老周他们排查到缓存命中率低的时候,还觉得是过期时间设太短了。打开配置一看,哦,所有商品缓存的过期时间都是30分钟,整整齐齐。我当时看到这个配置,直接笑出了声。你想想,所有商品的缓存都是整点半过期,那到了整点半的那一秒,是不是所有缓存同时失效?那一瞬间的请求,是不是全都会去查数据库?老周他们还没意识到问题,说我们加了随机偏移量啊?我让他把配置翻出来看——哦,所谓的随机偏移量,是所有缓存统一加5分钟,也就是所有缓存还是在整点半到整点四之间批量失效,等于没加。
热点key的过期时间,必须给每个单独加1到5分钟的随机偏移量,绝对不能批量设置统一的过期窗口。还有个更常见的坑,就是缓存击穿。比如某个爆款商品的缓存刚好过期了,同时过来200个请求,全去查数据库,然后全去更新缓存。我之前见过最离谱的操作,是有个团队为了防止这个,在缓存重建的时候加了分布式锁,结果锁的超时时间设成了10秒,刚好第一个重建缓存的请求卡了11秒,锁自动释放了,剩下的199个请求又全冲去数据库,直接把库打挂了。真的离谱。谁遇到谁头疼。
缓存从来不是兜底的万能药
很多团队上了缓存之后,就觉得性能问题解决了,什么请求都往缓存里塞。结果遇到最头疼的双写一致性问题,全懵了。比如用户刚在前端把收货地址改成了公司,结果下单的时候,系统从缓存里读到的还是之前的家里地址,快递寄错了地方,客诉赔了好几百块。老周他们之前的做法是,更新数据库之后,再删缓存。听着挺合理对吧?但你架不住并发啊。比如用户刚改完地址,数据库已经更了,还没来得及删缓存,刚好另一个请求过来查地址,读到了旧的数据库数据,然后把旧数据写回了缓存。得,这个缓存就一直是旧的,直到过期时间到了才会更新。那如果这个key的过期时间设的是24小时呢?那用户这一天的订单,全都会用旧地址。

不过话说回来,也不是说这种场景就没法解决。对于一致性要求高的业务,别搞什么延时双删那套玄学,直接上消息队列,数据库更新成功之后,发个消息到队列里,异步删缓存。就算删失败了,还有重试机制兜底。要是实在怕不一致,就给这类核心数据的缓存设个短一点的过期时间,最多5分钟,哪怕出问题,影响范围也可控。
下次你再上线新的缓存逻辑之前,先对着这三个问题过一遍:这个key的过期时间,会不会和其他key集中在同一个时间点失效?热点key失效的时候,有没有做并发控制,会不会把数据库打穿?缓存和数据库不一致的时候,业务能不能接受最坏的结果?别总想着找什么最优的缓存策略。适合自己业务的,才是最好的。