系统压测遇到的缓存击穿问题

背景

香港业务方计划在4月初做消费券活动,预计在4月有一波大促流量并且远超去年双12的流量水位,因此对香港业务方的核心链路重新做一次新流量水位的压测,保障当前集群能满足预期的流量水位需求。

经过两次实验性压测后,通过公式计算出新流量水位预计下上下游每个应用预计的集群大小和入口流量大小,并对各个上下游应用执行机房扩容和入口流量限流阈值调整。

万事俱备后,3月15日晚开始执行压测,按梯度逐渐将入口流量提升到45%时整个集群的资源使用情况十分稳定符合预期,但当流量水位从45%调整到55%时突然大量报错请求返回失败,因此中断压测排查问题。

排查

查看报错日志后,发现错误原因为DB连接池耗尽。压测的核心链路理论上应该全部使用缓存,于是去查看压测期间对应的监控记录,如下图,发现问题:

  1. 22:48时应用入口流量增长18%
  2. 22:48时远端缓存读取增长27%
  3. 22:48时DB查询增长1300%

应用入口流量增长后对远端缓存的读取量同比增长符合预期,但DB查询量却突然暴增13倍,第一反应是缓存击穿导致大量DB查询请求,然后致使数据库慢查询而耗尽DB连接池,最终导致当前的问题。

image

image

image

于是剩下的就只剩验证了,首先故障应用没有使用本地缓存,因此集群所有机器对每一个数据都共用一份远端缓存,一旦远端缓存过期失效,则瞬间集群所有集群读取不到缓存后会同时请求DB查询数据,符合DB查询增长的监控情况。

然后挑了一个单机节点查询故障时刻的DB查询日志,发现几乎全是对同一条记录的查询操作,也符合缓存击穿的猜想。

解决

确定了是因为远端缓存失效而导致的缓存击穿问题,后续的解决就很简单了。实际上这个问题去年压测的时候就已经暴露过,但由于去年水位远低于本次的水位,因此实际上去年在监控上看也仅仅是压测期间偶尔会出现DB调用次数的尖刺和DB耗时尖刺,无实际影响,所以一直把这个缓存击穿问题的优化方案给搁置了。

根本原因是应用使用的缓存均为远端缓存,无本地缓存。压测期间当远端缓存一旦过期,会导致瞬间大量DB查询请求,然后DB出现慢查询后耗尽数据库连接池,造成大量请求失败的问题。
因此需要对缓存做治理,防止出现远端缓存失效后,瞬时DB查询暴增的问题:

  1. 添加本地缓存,并且本地缓存过期时长有一定随机性,保证集群内每个单机的同一个本地缓存不会同时过期
  2. 添加远端缓存刷新逻辑,即使远端缓存还没过期,也定时强制刷新远端远程给远端缓存主动续期,防止热门远端缓存自动过期

改造后缓存读取流程如下图:

  1. 新增本地缓存,优先使用本地缓存,本地缓存不存在则使用远端缓存,远端缓存也不存在则执行数据库回查
  2. 本地缓存的有效期为5-10秒随机数,令整个集群的本地缓存不会同一时间过期
  3. 防止远端缓存过期,每个单机内存在一个计数器,每个周期(20-40秒随机数)内每个缓存的第一次读取强制执行数据库回查,实现远端缓存自动刷新,实现热门远端缓存永久不过期

image

未来

以上方案虽然解决了缓存过期后的击穿问题,但没有解决数据未预热场景下,突然流量导致的击穿问题。因此未来还需要实现预热能力,通过定时任务对数据库中可能为热点数据的记录做预热,提前加载到远端缓存中,防止突发流量击穿。

>