程序缓存优化:榨干系统性能的实战踩坑指南
你有没有遇过这种事?上线前压测好好的,流量一上来接口直接超时,查了大半天,发现数据库QPS干到好几千,原来九成请求全没走缓存。我去年双11前一周就踩过这个坑,负责的用户中心接口差点直接崩掉,全因为缓存策略写错了一行代码。今天把这些年摸出来的实战经验全抖出来,都是踩过坑踩出来的干货。
先搞懂:你真的用对缓存了吗?
很多新手的误区就是:加个Redis就算做了缓存优化。
哪有这么简单。缓存本来就是分层的,从CPU的一级二级缓存,到进程内本地缓存,到分布式缓存,再到浏览器缓存、CDN缓存,每一层放什么数据,本来就有讲究。不分层上来就堆Redis,反而平白多了好多次网络IO,性能还不如不加。
比如说系统里的省份字典、配置表这类,一年半载都变不了一次,访问量又大,放本地缓存不香吗?非要每次都跨网络去Redis拿一趟,这不就是浪费时间?
当然本地缓存也有坑,多实例部署的时候数据更新会不一致,这个只要对应场景加个消息广播通知更新,大部分场景就够用了。
https://obgeo.oss-cn-beijing.aliyuncs.com/pvc-articles/14c2010c-4a57-42e4-b814-f7aebf6bd1c1.jpg
程序多级缓存分层架构图
什么缓存雪崩、击穿、穿透,很多人背概念背的滚瓜烂熟,真写代码的时候还是忘。
就说缓存穿透,我见过有人碰到不存在的请求直接回源查库,也不缓存空值,结果被人用一堆不存在的ID刷,直接把数据库打崩。也有人缓存了空值,过期时间设的和正常数据一样长,该被打还是被打,其实空值设个几分钟过期就够了,多大点事。
最容易出效果的几个缓存优化实战技巧
我试过这么多优化方法,这几个改完当天就能看到命中率涨上去,性能提升肉眼可见。
第一个,按热点分层放缓存,不要把所有数据都扔同一个地方。
不变的静态资源直接扔CDN,整页静态化可以放Nginx本地缓存,接口里的热点数据,比如Top20%的访问数据,放本地Caffeine缓存,剩下的全量数据再放Redis。
说实话,换了Caffeine之后,我手上项目的本地缓存命中率直接从87%涨到了95%,吞吐量涨了快一倍,比老的Guava好太多了,谁用谁知道。
第二个,热点缓存做预加载,设永不过期加主动更新。
就说电商平台的Top100热门商品,或者社交平台的头部大V信息,本来就是访问量最高的,你提前把这些数据预加载到所有实例的本地缓存,不设置过期时间,数据更新的时候主动发通知让所有实例刷新缓存不就完了?干嘛要让它过期自动回源,给系统添不必要的波动?
第三个,缓存过期时间一定要打散,别搞统一过期。
这个很多人都知道,可就是嫌麻烦不做。同一个类型的缓存全设成30分钟过期,一到点集体失效,所有请求全打去数据库,这不崩才怪啊!不就是加个一行代码的随机偏移吗?30分钟±5分钟,很难吗?
我去年双11踩的那个坑,就是所有用户缓存全设了1小时整过期,高峰点一到集体失效,数据库CPU直接干到100%,告警短信直接把我手机炸卡了,临时加了随机偏移才救回来,现在想起来都后怕。
https://obgeo.oss-cn-beijing.aliyuncs.com/pvc-articles/36f9c73d-b2e2-4767-8cd0-2c6c8d254154.jpg
Caffeine与Guava缓存性能对比测试图
这些冷门坑,90%的开发者都踩过
https://obgeo.oss-cn-beijing.aliyuncs.com/pvc-articles/d0499a38-9354-4331-a2d6-329feecf2d61.jpg
这些冷门坑,90%的开发者都踩过
讲几个我踩过的冷门坑,别看着简单,中招的人真不少。
第一个,大key不要往缓存里塞。你把一个十几MB的对象整个扔Redis,每次存取都慢,还占带宽,拖慢整个实例的性能。别说Redis了,本地缓存放太多大key,JVMGC都能给你卡爆。
大key能拆就拆,你只需要用户的昵称头像,就只缓存这两个字段,干嘛把用户的所有信息整个都塞进去?
第二个,不要为了强一致过度设计。缓存和数据库双写不一致的问题吵了十几年,很多人上来就要搞分布式锁、搞事务,其实大部分场景根本不需要强一致。用户改了个个人简介,晚个几百毫秒生效,没人会在乎你。真要一致性,更完数据库删缓存,加个短过期时间兜底,足够99%的场景用了。搞那么复杂,出问题的时候排查都没法排,纯属给自己找麻烦。
第三个,不是什么数据都值得缓存。我见过有人把用户每次请求的随机临时数据都往缓存塞,过期时间还设的老长,最后Redis存了几十GB没用的数据,慢查询一大堆,性能比没加缓存还差。
缓存本来就是为热点数据服务的,二八定律摆在这,80%的请求只访问20%的数据,你把那20%管好就行,剩下的冷数据让它查数据库怎么了?又死不了。
不过话说回来,程序缓存优化从来不是越复杂越好。
小项目就几个实例,访问量也不高,本地缓存足够用,没必要搭什么Redis集群,给自己增加运维成本。大流量的项目,你也先去看看当前缓存命中率是多少,哪部分数据不命中,针对性优化,不要上来就堆一堆花里胡哨的架构。
最后说一句,别忘了加监控,把缓存命中率、命中次数、失效次数都监控起来,没有数据的优化都是瞎蒙。 说得很有道理,很多观点都说到点子上了,希望以后能多看到这类帖子。 感谢楼主无私分享经验,少走了很多弯路,对我们帮助特别大。 不管是内容还是排版都很用心,看得出来楼主花了不少时间,必须支持。 很多细节都考虑到了,看得出来是真正懂行的人在认真分享。 这个问题困扰我很久了,看了你的帖子终于有思路了,非常感谢。 这样的优质帖子越多越好,希望论坛能多一些这样真诚的交流。 这样用心的帖子不多见,必须顶上去让更多人看到优质内容。 认真看完了整篇内容,感觉受益匪浅,期待楼主后续更多优质的分享。 讲得很透彻,把复杂的问题简单化,理解起来轻松了很多。 已经推荐给朋友来看了,好东西就应该一起分享一起学习。 观点很成熟,分析也很理性,值得大家静下心来认真看一看。 看完感觉打开了新思路,以后遇到类似情况知道该怎么处理了。 支持楼主继续更新,这么好的内容值得让更多人看到和学习。 感谢楼主愿意花时间分享,让我们这些后来者能少踩很多坑。
页:
[1]