首页 > 其他分享 >经验之谈:我为什么选择了这样一个激进的缓存大Key治理方案

经验之谈:我为什么选择了这样一个激进的缓存大Key治理方案

时间:2024-05-06 09:04:34浏览次数:23  
标签:SKU 缓存 Key 删除 经验之谈 key 白名单

一、引言

本文将结合我的一次Redis大Key的治理经验,来浅谈一下缓存大Key的治理方案选择。文中主要包括缓存大Key基础知识大Key治理方案选择大Key治理案例等,适合有一定开发经验的开发者阅读,希望对大家有帮助。

二、缓存大Key基础知识

2.1 大Key的标准

集合类型元素数量>5000或者单个value大于1M。当然这个标准不是绝对的,而是需要根据具体的业务场景和Redis集群的实际情况来灵活调整

2.2 大Key带来的风险

  • 大key发生热点访问,响应积压在服务端后内存暴涨,最终导致服务端被杀造成数据丢失
  • 严重影响 QPS、TP99等指标,对大Key进行的慢操作会导致后续的命令被阻塞,从而导致一系列慢查询。
  • hgetall、smembers 等时间复杂度O(N)的命令使用不当,容易造成CPU使用率过高。
  • 集群各分片内存使用不均。某个分片占用内存较高或OOM,发送缓存区增大等,导致该分片其他Key被逐出,同时也会造成其他分片的资源浪费。
  • 集群各分片的带宽使用不均。某个分片被流控,其他分片则没有这种情况,且影响宿主机上的其它应用。
  • 数据迁移失败 过大的Key(如超过1G),在迁移、缩容、扩容,主从全量同步在序列化过程中,内存上涨,数据同步失败,且存在数据丢失风险。

三、缓存大Key治理方案

3.1事前预防

事前预防的基本思路是在缓存设计的时候,缓存中只存储必要的数据,且需要考虑将存储空间变小,具体的手段有:

  1. 对于JSON类型,可以删除不使用的Field;或者使用@JsonProperty 注解让 FiledName 字符集缩小;
  2. 采用Protobuf等压缩算法,利用时间换空间;
  3. 对于集合类型,设计上尽量避免整存整取;

3.2 事中监控

事中做好监控,对于缓存大Key,早发现,早治理;

3.3 事后

缓存大Key已经存在,那么应该怎么办?分2种情况进行考虑:

  1. 评估这些大Key是否还有存在的意义和价值,是否可以删除,对于可以删除的可以使用SCAN命令进行循序渐进式删除大Key;对于不可以直接删除的这种,是否可以将数据转移到其他的介质进行存储;
  2. 对于不能直接删除的大Key,进行“分而治之”,再通俗一点就是“拆”,将大Key进行拆分
  • String类型的大Key:可以尝试将对象分拆成几个Key-Value,使用MGET或者多个GET组成的pipeline获取值,分拆单次操作的压力,对于集群来说可以将操作压力平摊到多个分片上,降低对单个分片的影响。
  • 集合类型的大Key,每次只需操作部分元素:将集合类型中的元素分拆。以Hash类型为例,可以在客户端定义一个分拆Key的数量N,每次对HGET和HSET操作的field计算哈希值并取模N,确定该field落在哪个Key上。

四、缓存大Key治理案例

4.1 系统架构和业务场景说明

有这样一个电商活动管理系统,系统有2个应用:后台管理端、运营端;(后台管理端是管理人员进行操作;运营端可以创建营销活动)存储使用的是MySQL和Redis;

系统中有一个这样的场景,简单来说就是,后台管理端可以添加sku白名单,这个白名单是以“商家ID + skuId”为一条记录写入MySQL中的;运营端在创建营销活动,需要上传sku,上传的时候需要读取sku白名单进行校验。

系统现状是,在查询sku白名单的时候使用了Redis缓存。缓存模式采用的是旁路缓存,添加SKU白名单的流程是,写入数据库,并删除缓存;读取SKU白名单的流程是,先从缓存读取,缓存中没有则读取数据库,并将结果写入缓存。缓存的数据类型是String,value为数据库中的全量SKU白名单记录,是以JSON字符串存储的。

旁路缓存:读取缓存、读取数据、更新缓存和操作都是在应用程序中完成的。

但是随着业务的发展,添加的SKU白名单逐渐增多,发展成为了缓存大Key。截止治理之前,数据库的配置表中存在1w+条sku白名单记录,也就是说Redis的一个String结果存储了1w+条的白名单记录。

这里也许有人问,一张表中存储1w+条数据,也是没有压力的啊,其实这张配置表中不仅有商家SKU白名单配置,还包括系统中其他所有的配置信息存储,数据量还是可观的。

4.2 一种激进的治理方案

该业务场景中,缓存大Key是不可以直接进行删除处理的,处理思路是将其拆分为一些小的Key。

分析场景,商家可以登录运营端进行创建营销活动,在创建营销活动和上传SKU的过程中,只需要查询该商家的SKU白名单,那么存储和查询全部都是不必要的,那么可以将全量白名单数据按照商家维度进行拆分存储和查询。

关于缓存结构的选择,我使用的是Hash结构(新缓存),key与原缓存String结构 key 相同,field为商家ID,value为sku白名单列表。大概流程图如下:

新缓存Hash结构key与原缓存String结构 key 完全相同,意味着新的Hash结构缓存和旧的String缓存是无法共存的,只能选择其一,意味着不存在百分比切量的过程,一步到位切量,你就说是否激进?

这样激进的方案是基于什么的考量呢?

  1. 夜间流量几乎为0,且缓存切换操作秒级完成,风险可控。运营端系统,商家创建促销活动基本都是工作时间,晚上和凌晨几乎没有流量;而且,新旧缓存切换可以秒级完成:①运营端添加测试SKU白名单,删除缓存;②将开关切到新缓存;风险可控。
  2. 方案支持秒级回滚.①运营端添加测试SKU白名单,删除缓存;②将开关切到旧缓存。
  3. 代码改动和上线部署工作量小。这个方案可以保持管理后端删除缓存的逻辑不变,也就是在添加SKU白名单的时候还是删除所有的白名单缓存;只需要改动运营后端代码,且上线部署只需要部署运营端。
  4. 虽然管理后端删除缓存为全量删除,存在管理端添加商家A的SKU白名单,导致缓存中的商家B、商家C等的白名单数据也被删除(其实是无需删除的),综合评估可以暂时保持现状,后续优化

4.3 更加通用的治理方案

4.3.1 三个阶段

更加通用的缓存大key治理方案,包括双写、双读对比和读写新key等阶段:

  1. 双写阶段

在不影响现有业务的情况下,将新数据同时写入旧key和新key;

  1. 双读对比阶段

验证新key的数据与旧key的数据是否一致,并准备切换读操作到新key;

  1. 读写新key阶段

将所有读写操作都切换到新key,并废弃旧key;需要注意删除大key时要避免阻塞Redis服务。

4.3.2 注意事项

  • 数据一致性:在整个治理过程中,需要确保数据的一致性。特别是在双写和双读对比阶段,要仔细验证数据是否一致。
  • 性能影响:在双写和双读对比阶段,由于需要同时操作旧key和新key,可能会对性能产生一定影响。因此,需要在业务低峰期进行这些操作,并监控系统的性能指标。
  • 错误处理:在治理过程中,可能会遇到各种错误情况(如数据不一致、网络问题等)。需要制定完善的错误处理机制来确保系统的稳定性和可用性。
  • 备份和恢复:在执行治理操作之前,建议对Redis数据进行备份。以便在出现问题时可以快速恢复数据。

五、小结

本文是一个真实的线上缓存大Key治理案例,区别于通用的治理方案,我选择了一种激进和简单的治理策略。对于缓存大Key进行简单总结一下,对于使用缓存,需要考虑缓存大Key问题,设计和开发过程中尽量避免;如果已经出现,考虑删除,如果不可以删除,考虑拆分。可以在权衡之下,选择最适合自己的方案。

一起学习

欢迎各位在评论区或者私信我一起交流讨论,或者加我主页weixin,备注技术渠道(如博客园),进入技术交流群,我们一起讨论和交流,共同进步!

也欢迎大家关注我的博客园、公众号(码上暴富),点赞、留言、转发。你的支持,是我更文的最大动力!

标签:SKU,缓存,Key,删除,经验之谈,key,白名单
From: https://www.cnblogs.com/sgh1023/p/18174067

相关文章

  • go高并发之路——缓存击穿
    缓存击穿,Redis中的某个热点key不存在或者过期,但是此时有大量的用户访问该key。比如xxx直播间优惠券抢购、xxx商品活动,这时候大量用户会在某个时间点一同访问该热点事件。但是可能由于某种原因,redis的这个热点key没有设置,或者过期了,那么这时候大量高并发对于该key的请求就得不到red......
  • 如何基于surging跨网关跨语言进行缓存降级
    概述      surging是一款开源的微服务引擎,包含了rpc服务治理,中间件,以及多种外部协议来解决各个行业的业务问题,在日益发展的今天,业务的需求也更加复杂,单一语言也未必能抗下所有,所以在多语言行业解决方案优势情况下,那么就需要多语言的协同研发,而对于协同研发环境下,统一配置......
  • spring三级缓存
    第一级缓存:singletonObjects第二级缓存:earlySingletonObjects第三级缓存:singletonFactories先从“第一级缓存”找对象,有就返回,没有就找“二级缓存”;找“二级缓存”,有就返回,没有就找“三级缓存”;找“三级缓存”,找到了,就获取对象,放到“二级缓存”,从“三级缓存”移除。 在第......
  • 配置session——缓存
    1、服务器+redis安装启动2、djangoa、安装链接redis包pipinstalldjango-redisb、settings.py```MIDDLEWARE=['django.contrib.sessions.middleware.SessionMiddleware',]#sessionSESSION_ENGINE='django.contrib.sessions.backends.cache'SESSION_CAC......
  • redis缓存和业务应用了解
    转自:https://tech.meituan.com/2017/03/17/cache-about.html1.介绍在主页中显示最新的项目列表:Redis使用的是常驻内存的缓存,速度非常快。LPUSH用来插入一个内容ID,作为关键字存储在列表头部。LTRIM用来限制列表中的项目数最多为5000。如果用户需要的检索的数据量超越这个缓存容......
  • 缓存特征了解
    转自:https://tech.meituan.com/2017/03/17/cache-about.html1.介绍1.1命中率命中率=返回正确结果数/请求缓存次数,命中率问题是缓存中的一个非常重要的问题,它是衡量缓存有效性的重要指标。命中率越高,表明缓存的使用率越高。1.2最大元素缓存中可以存放的最大元素的数量,一旦缓......
  • 高并发缓存架构实战和优化
    参考资料:图灵课堂-https://vip.tulingxueyuan.cn 中小公司Redis缓存架构以及线上问题分析直接写入数据库,然后更新redis;正常低并发情况下,这种情况是暂时不会出现问题的,因为并发量并不高,不会出现各种各样的并发问题。大厂线上大规模商品缓存数据冷热分离实战使用缓存,主要是......
  • 浏览器缓存
    浏览器缓存基本的原理就是:浏览器每次发起请求,都会先在浏览器缓存中查找该请求的结果以及缓存标识浏览器每次拿到返回的请求结果都会将该结果和缓存标识存入浏览器缓存中按位置分类ServerworkerServiceWorker是运行在浏览器背后的独立线程,一般可以用来实现缓存功能。使......
  • js逆向实战之某证信Accept-Enckey参数加密解析
    url:https://webapi.cninfo.com.cn/#/marketDataDate分析过程抓包,主要关注图中标记的数据包,它的回显数据是我们所需要的。但在该数据包的请求中有一个Accept-Enckey参数是经过加密的,需要知道其加密的逻辑。全局搜索sysapi/p_sysapi1007,只有一处符合的。找到对应地方,......
  • uniapp 清除缓存
    //清除所有数据clearAppUserData(){plus.android.importClass("android.app.ActivityManager");varContext=plus.android.importClass("android.content.Context");varam=plus.android......