dbaplus社群 05月20日 13:22
出了大事故!才发现缓存大热key的致命陷阱……
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文通过一个实际案例,深入探讨了缓存大热Key和缓存击穿问题。该系统在大促期间因活动缓存过大,导致Redis带宽受限,引发缓存雪崩。针对此问题,文章提出了大Key治理、压缩算法、缓存回源优化以及监控Redis配置等解决方案。此外,还强调了在系统设计阶段考虑缓存策略、进行压力测试和性能评估的重要性,以避免类似问题再次发生。

🔥 **案例回顾:** 某系统大促期间,因活动缓存内容过大,导致Redis访问量骤增,可用率断崖式下降,最终系统服务不可用。问题核心在于大热Key和缓存击穿。

🔑 **问题分析:** 尽管使用了本地JVM缓存,但运营人员上线活动瞬间,大量请求直击Redis,加之单个Key过大(1.5M),迅速达到Redis单分片带宽限流阈值(200M),导致Redis阻塞,引发雪崩。

🛠️ **解决方案:** 包括更换序列化方式(JSON -> Protostuff,降低Key大小至0.5M)、使用压缩算法(如gzip,进一步压缩至17k)、本地缓存Miss时增加线程锁、以及监控和优化Redis配置。

🛡️ **预防措施:** 在系统设计阶段充分考虑缓存使用场景和数据特性,避免盲目使用大Key缓存;上线前进行压力测试和性能评估;定期进行系统优化和升级。

京东零售 曹志飞 2025-05-20 07:15 广东

常见但易被忽视:缓存大热key和缓存击穿问题。

导读:在现代软件架构中,缓存是提高系统性能和响应速度的重要手段。然而,如果不正确地使用缓存,可能会导致严重的线上事故,尤其是缓存的大热key问题更是老生常谈。本文将探讨一个常见但容易被忽视的问题:缓存大热key和缓存击穿问题。我们将从一个真实案例入手,分析其原因,并提供解决方案和预防措施。

一、案例描述

某系统在大促期间,遇到了一个严重的线上事故。业务人员在创建一个大型活动,该大型活动由于活动条件和活动奖励比较多,导致生成的缓存内容非常大。活动上线后,系统就开始出现各种异常告警,核心UMP监控可用率由100%持续下降到20%,系统访问Redis的调用次数和查询性能也断崖式下降,后续更是产生连锁反应影响了其他多个核心接口的可用率,导致整个系统服务不可用。

二、原因分析

在这个系统中,为了提高查询活动的性能,我们开发团队决定使用Redis作为缓存系统。将每个活动信息作为一个key-value存储在Redis中。由于业务需要,有时候业务运营人员也会创建一个非常庞大的活动,来支撑双十一期间的各种玩法。针对这种庞大的活动,我们开发团队也提前预料到了可能会出现的大key和热key问题,所以在查询活动缓存之前增加了一层本地jvm缓存,本地jvm缓存5分钟,缓存失效后再去回源查询Redis中的活动缓存,本以为会万无一失,没想到最后还是出了问题。

查询方法伪代码

    ActivityCache present = activityLocalCache.getIfPresent(activityDetailCacheKey);

    if (present != null) {

        ActivityCache activityCache = incentiveActivityPOConvert.copyActivityCache(present);

        return activityCache

    }

    ActivityCache remoteCache = getCacheFromRedis(activityDetailCacheKey);

    activityLocalCache.put(activityDetailCacheKey, remoteCache);

    return remoteCache;

    查询活动缓存流程如上图所示,为什么加了本地缓存还是出了问题?

    这里其实就存在着第一个缓存陷阱:缓存击穿问题。首先解释一下什么是缓存击穿;缓存击穿(Cache Miss)是指在高并发的系统中,如果某个缓存键对应的值在缓存中不存在(即缓存失效),那么所有请求都会直接访问后端数据库,导致数据库的负载瞬间增加,可能会引发数据库宕机或服务不可用的情况。所以在本次事故里边,运营人员审批活动上线的一瞬间,活动缓存只是写入到了Redis缓存中,但是本地缓存还都是空的,所以此时就会有大量请求来同时访问Redis。

    按照以往经验,Redis缓存都是纯内存操作,查询性能可以满足大量请求同时查询活动缓存,就在此时我们却陷入了第二个缓存陷阱:网络带宽瓶颈;Redis的高并发性能毋庸置疑,但是我们却忽略了一个大key和热key对网络带宽的影响,本次引发问题的大热key大小达到了1.5M,经过事后了解京东Redis对单分片的网络带宽也有限流,默认200M,根据换算,该热key最多只能支持133次的并发访问。所以就在活动上线的同一时刻,加上缓存击穿的影响,迅速达到了Redis单分片的带宽限流阈值,导致Redis线程进入阻塞状态,以至于所有的业务服务器都无法查询Redis缓存成功,最终引发了缓存雪崩效应。

    三、解决方案

    为了解决这个问题,开发团队采取了以下措施:

    1、大key治理:更换缓存对象序列化方法,由原来的JSON序列化调整为Protostuff序列化方式。治理效果:缓存对象大小由1.5M减少到了0.5M。

    2、使用压缩算法:在存储缓存对象时,再使用压缩算法(如gzip)对数据进行压缩,注意设置压缩阈值,超过一定阈值后再进行压缩,以减少占用的内存空间和网络传输的数据量。压缩效果:500k压缩到了17k。

    3、缓存回源优化:本地缓存miss后回源查询Redis增加线程锁,减少回源Redis并发数量。

    4、监控和优化Redis配置:定期监控Redis网络传输情况,根据实际情况调整Redis的限流配置,以确保Redis的稳定运行。

    治理后业务伪代码如下:

      ActivityCache present = activityLocalCache.get(activityDetailCacheKey, key -> getCacheFromRedis(key));

      if (present != null) {                

          return present;

      }

        /**

        * 查询二进制缓存

        *

        * @param activityDetailCacheBinKey

        * @return

        */

        private ActivityCache getBinCacheFromJimdb(String activityDetailCacheBinKey) {

            List<byte[]> activityByteList = slaveCluster.hMget(activityDetailCacheBinKey.getBytes(),"stock".getBytes());

            if (activityByteList.get(0) != null && activityByteList.get(0).length > 0) {

                byte[] decompress = ByteCompressionUtil.decompress(activityByteList.get(0));

                ActivityCache activityCache = ProtostuffUtil.deserialize(decompress, ActivityCache.class);

                if (activityCache != null) {

                    if (activityByteList.get(1) != null && activityByteList.get(1).length > 0) {

                        activityCache.setAvailableStock(Integer.valueOf(new String(activityByteList.get(1))));

                    }

                    return activityCache;

                }

            }

        return null;

        四、预防措施

        为了避免类似的问题再次发生,开发团队采取了以下预防措施:

        设计阶段考虑缓存策略:在系统设计阶段,充分考虑缓存的使用场景和数据特性,避免盲目使用大key缓存。

        进行压力测试和性能评估:在上线前,进行充分的压力测试和性能评估,模拟高并发和大数据量的情况,及时发现和解决潜在问题。

        定期进行系统优化和升级:随着业务的发展和技术的进步,定期对系统进行优化和升级,引入新的技术和工具来提高系统的性能和稳定性。

        五、结论

        缓存大key和热key是缓存使用中常见的陷阱,千万不要心存侥幸,否则会引发严重的线上事故。通过本文的案例分析和解决方案,我们希望能够帮助读者更好地理解和应对这个问题。记住,合理使用缓存是提高系统性能的关键,而不是简单地将所有数据都存储在缓存中。

        作者丨曹志飞

        来源丨公众号:京东技术(ID:jingdongjishu)

        dbaplus社群欢迎广大技术人员投稿,投稿邮箱:editor@dbaplus.cn

        阅读原文

        跳转微信打开

        Fish AI Reader

        Fish AI Reader

        AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

        FishAI

        FishAI

        鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

        联系邮箱 441953276@qq.com

        相关标签

        缓存 大热Key 缓存击穿 Redis 性能优化
        相关文章