当前位置:首页 > 游戏动态 > 正文

小鱼课堂:掌握三级缓存的核心用途,解决高并发场景下的性能瓶颈

哎,你说现在这系统啊,动不动就卡死,用户一多页面就转圈圈,真是头疼,我们团队之前就遇到过,促销活动一来,服务器直接挂掉,那叫一个狼狈,后来折腾了好久,才算是搞明白了三级缓存这玩意儿,真不是书上写的那么死板,它是个活生生的、能救急的家伙。

你想啊,最简单的,没有缓存的时候,每个请求都直接怼到数据库上,数据库哪受得了这个?它就是个老实巴交的账房先生,一笔一划记账可以,你一下子涌进来一万个人问他同一个问题:“这个商品还有没有货?”他得翻一万遍账本,不累趴下才怪,第一级缓存,我们叫它本地缓存,比如用Caffeine或者简单的ConcurrentHashMap,就相当于给每个服务实例配了个小秘书,这个秘书记性特别好,最近谁问了什么问题,答案是什么,她门儿清,同一个问题短时间内再有人问,她直接就回答了,根本不用去打扰账房先生,这速度,快得飞起,纳秒级别,但问题也来了,这小秘书是每个服务实例私有的,比如你有十个服务实例,负载均衡把请求分散了,第一个用户问A实例,秘书记下了答案;第二个用户可能被分到B实例,B的秘书不知道啊,还得去问数据库,这就造成了缓存穿透,十个秘书可能轮流去烦账房先生,账房先生还是压力山大。

小鱼课堂:掌握三级缓存的核心用途,解决高并发场景下的性能瓶颈

这时候就得请出第二级缓存了,也就是分布式缓存,比如Redis,它像个公共布告栏,放在所有服务实例都能访问的地方,不管哪个实例的秘书,从账房先生那里问到一个答案,除了自己记在小本本上,还会大声公布在布告栏上,这样,其他实例的秘书再遇到同样的问题,先看自己小本本(本地缓存),没有的话,就去瞅一眼公共布告栏(Redis),大概率就能找到答案,就不用再去问账房先生了,Redis是基于内存的,速度也很快,毫秒级,这布告栏还有个好处,它能设置个过期时间,比如五分钟,这样信息不会一直旧着,到时候自动撕掉,迫使大家去问一次最新的,但…这里又有个坑,就是缓存雪崩,你想,如果布告栏上很多信息同时到期,一下子全被撕掉了,恰巧这时候又来了一大波请求,所有秘书发现小本本没有,布告栏也没有,我的天,那场面,所有请求瞬间全砸向可怜的账房先生,数据库可能直接就崩了,所以我们得让这些信息的过期时间错开点,别扎堆。

小鱼课堂:掌握三级缓存的核心用途,解决高并发场景下的性能瓶颈

那有没有更保险的办法呢?嗯,我们想到了第三级,也是最根本的一级,就是数据库本身,但直接查数据库太慢,所以我们得给它加个“预加载”的机制,这就是缓存预热异步更新的思路,我们可以在后台跑个任务,在布告栏(Redis)里的信息快要过期但还没过期的时候,主动去数据库查一下最新数据,悄悄更新到布告栏上,这样,大部分请求根本感觉不到数据有过期这回事,布告栏上的信息几乎是常新的,甚至,我们可以用消息队列,当数据库里的核心数据真的变了(比如库存变了),主动发个通知到队列里,让各个服务实例去更新自己的本地缓存和Redis缓存,这就相当于账房先生自己记完账,主动广播一声:“喂,大家注意,XX商品库存变了,是100件!”所有秘书听到后,赶紧更新自己的记录,这样,缓存和数据库的一致性就高多了。

说起来简单,但真做起来,细节多得要命,本地缓存和Redis缓存更新顺序搞反了,可能会读到脏数据,还有,布告栏(Redis)万一自己挂了呢?所以我们还得考虑Redis的集群、哨兵机制,保证高可用,有时候为了极致性能,甚至会在应用和Redis之间再加一层,比如用Nginx做代理缓存,应对海量的静态资源读取…… 这三级缓存,就像一套组合拳,每一级都有它的用处,也都有它的弱点,本地缓存快但容量小、不一致;Redis折中了速度和容量,但本身也是个需要保护的远程服务;数据库是最终的真相来源,但速度是瓶颈。

我现在还记得,我们第一次把这套东西跑通的那个晚上,看着监控图上那条原本像过山车一样的数据库QPS曲线,终于变得平缓下来,心里那块石头才算落地,这东西吧,没有银弹,得根据你自己的业务场景去调整,比如哪些数据放本地,过期时间设多长,更新策略是啥…… 都得慢慢试,但只要你理解了每一级为啥存在,能解决啥问题,又会带来什么新麻烦,你就能玩转它了,说白了,三级缓存就是个权衡的艺术,在速度、一致性、复杂度之间找一个当前业务最能接受的平衡点,搞技术,有时候就是这么个不断折腾、不断妥协的过程,但解决问题那一刻的爽快感,也是真让人上瘾。