当前位置: 首页 > news >正文

Redis中的bigkey的介绍及影响

目录

1、定义分类

1.1、介绍

1.2、分类

1.3、应用场景

1.4、删除BigKey

2、作用影响

2.1、Redis阻塞

2.2、​内存碎片化:

2.3、​网络带宽阻塞:

2.4、持久化风险

2.5、集群运维困境

3、定位BigKey命令

3.1、redis-cli命令

3.2、SCAN命令

3.3、使用 RdbTools

4、优化方案

4.1、合理设计key

4.2、拆分大key

4.3、设置合理的过期时间

4.4、启用内存淘汰策略

4.5、使用数据压缩

4.6.、渐进式删除

4.7、数据迁移

4.8、优化数据结构

4.9、监控与预防


前言

        Redis中的大key问题指的是某个key对应的value值所占的内存空间比较大,这会导致Redis的性能下降、内存不足、数据不均衡以及主从同步延迟等一系列问题。

        大key的具体定义并不固定,通常认为字符串类型的key对应的value值占用空间大于1MB,或者集合类型的key元素数量超过1万个,就视为大key。

与热key不同的是:

        把访问频率高的Key,称为热Key。

        比如突然有几十万的请求去访问redis中某个特定的Key,那么这样会造成redis服务器短时间流量过于集中,很可能导致redis的服务器宕机。


1、定义分类

什么是BigKey?为什么它成为Redis的性能杀手?

1.1、介绍

        BigKey指在Redis中key对应的value占用内存元素数量超出业务合理阈值的键值对。

1.2、分类

        分为字符串类型集合类型

1、​String类型:

String 类型的值大于 10 KB(value最大值为512MB);


2、​集合类型(Hash/List/Set/ZSet)​:

元素数量超过 ​5000个​(阿里云规范建议值)或内存达百万级(最大存放2^32-1个元素)。

1.3、应用场景

1、redis数据结构使用不恰当

        将Redis用在并不适合其能力的场景,造成Key的value过大,如使用String类型的Key存放大体积二进制文件型数据。

​复合场景:如未分片的用户行为日志、商品详情页缓存(含图文描述+评价列表)。


案例:

        某电商平台将单个商品的完整详情(包含20个字段的JSON数据)以String类型存储,导致每个Key大小超过2MB,最终引发查询延迟激增。

2、未及时清理垃圾数据

        没有对无效数据进行定期清理,造成如HASH类型Key中的成员持续不断的增加。即一直往value塞数据,却没有删除机制,value只会越来越大

3、对业务中key预估不准确

        业务上线前规划设计考虑不足没有对Key中的成员进行合理的拆分,造成个别Key中的成员数量过多。

4、明星、网红的粉丝列表、某条热点新闻的评论列表

        假设我们使用List数据结构保存某个明星/网红的粉丝,或者保存热点新闻的评论列表,因为粉丝数量巨大,热点新闻因为点击率、评论数会很多,这样List集合中存放的元素就会很多,可能导致value过大,进而产生Big Key问题。

1.4、删除BigKey


1、分批次删除

如果是集合类型,则遍历BigKey的元素,先逐个删除子元素,最后删除BigKey。

2、异步删除

        从 Redis 4.0 版本开始,可以采用异步删除法,用 unlink 命令代替 del 来删除。这样 Redis 会将这个 key 放入到一个异步线程中进行删除,这样不会阻塞主线程。


2、作用影响

可能带来的网络阻塞、内存占用和性能影响。

如下图所示:


2.1、Redis阻塞

关于redis的线程模型,可参考:关于多线程的Redis模型_redis线程模型-CSDN博客https://blog.csdn.net/weixin_50055999/article/details/147977886?spm=1011.2415.3001.5331

单线程阻塞:执行HGETALL或DEL耗时超过10ms即会影响其他请求。


​极端案例:某社交平台删除一个包含10万成员的ZSet时,主线程阻塞达2.3秒,触发服务熔断
内存与网络双重压力。


2.2、​内存碎片化:

每个key在存放过程中,会先进行hash函数取模去判断分区,具体可参考:

深入了解redis的哈希槽的知识_redis 哈希槽-CSDN博客https://blog.csdn.net/weixin_50055999/article/details/148104826?spm=1011.2415.3001.5331

BigKey所在的Redis实例内存使用率远超其他实例,无法使数据分片的内存资源达到均衡。


2.3、​网络带宽阻塞:

1MB的Key每秒访问1000次将产生1GB/s流量,千兆网卡直接打满。


2.4、持久化风险

关于redis的持久化,可参考:对Redis组件的深入探讨_redis 磁盘 内存-CSDN博客https://blog.csdn.net/weixin_50055999/article/details/147757520?spm=1011.2415.3001.5331
​AOF追加延迟:Always策略下写入大Key导致fsync耗时激增,主线程卡顿
​RDB生成失败:某游戏公司因一个50MB的排行榜Key导致bgsave超时,主从同步中断

1、对AOF日志的影响
Redis 提供了 3 种 AOF 日志写回硬盘的策略,分别是:

1、Always:

        「总是」,所以它的意思是每次写操作命令执行完后,同步将 AOF 日志数据写回硬盘;


2、Everysec:

        「每秒」,所以它的意思是每次写操作命令执行完后,先将命令写入到 AOF 文件的内核缓冲区,然后每隔一秒将缓冲区里的内容写回到硬盘;


3、No:

        意味着不由 Redis 控制写回硬盘的时机,转交给操作系统控制写回的时机,也就是每次写操作命令执行完后,先将命令写入到 AOF 文件的内核缓冲区,再由操作系统决定何时将缓冲区内容写回硬盘。

总结:

Always 策略就是每次写入 AOF 文件数据后,就执行 fsync() 函数;
Everysec 策略就会创建一个异步任务来执行 fsync() 函数;
No 策略就是永不执行 fsync() 函数;

        当 AOF 写回策略配置了 Always 策略,如果写入是一个大 Key,主线程在执行 fsync() 函数的时候,阻塞的时间会比较久,因为当写入的数据量很大的时候,数据同步到硬盘这个过程是很耗时的。

        当使用 Everysec 策略的时候,由于是异步执行 fsync() 函数,所以大 Key 持久化的过程(数据同步磁盘)不会影响主线程。

        当使用 No 策略的时候,由于永不执行 fsync() 函数,所以大 Key 持久化的过程不会影响主线程。

2、对AOF重写和RDB的影响


        AOF 重写机制和 RDB 快照(bgsave 命令)的过程,都会分别通过 fork() 函数创建一个子进程来处理任务。会有两个阶段会导致阻塞父进程(主线程):

        创建子进程的途中,由于要复制父进程的页表等数据结构,阻塞的时间跟页表的大小有关,页表越大,阻塞的时间也越长;


        创建完子进程后,如果父进程修改了共享数据中的大 Key,就会发生写时复制,这期间会拷贝物理内存,由于大 Key 占用的物理内存会很大,那么在复制物理内存这一过程,就会比较耗时,所以有可能会阻塞父进程。


2.5、集群运维困境

关于redis的部署,可参考:谈谈Redis缓存和数据库一致性的处理方案_redis缓存如何与数据库保持一致-CSDN博客https://blog.csdn.net/weixin_50055999/article/details/147782383?spm=1011.2415.3001.5331
​数据倾斜:某个分片存储3个10GB的Key,其他节点内存利用率不足20%
​扩容失效:迁移BigKey时因超时触发slot迁移重试循环


3、定位BigKey命令

定位bigkey的方案如下:

3.1、redis-cli命令

示例:

redis-cli -h 127.0.0.1 -p6379 -a "password" -- bigkeys# 扫描耗时型操作,建议在从节点执行
redis-cli -h 127.0.0.1 -p 6379 --bigkeys -i 0.1# 输出示例
[00.00%] Biggest string found 'user:1024:info' has 12 bytes
[12.34%] Biggest hash   found 'product:8888:spec' has 10086 fields

注意事项:

        最好选择在从节点上执行该命令。因为主节点上执行时,会阻塞主节点;
        如果没有从节点,那么可以选择在 Redis 实例业务压力的低峰阶段进行扫描查询以免影响到实例的正常运行;或者可以使用 -i 参数控制扫描间隔,避免长时间扫描降低 Redis 实例的性能。


不足之处:

        只能返回每种类型中最大的那个 bigkey,无法得到大小排在前 N 位的 bigkey;对于集合类型来说,只统计集合元素个数的多少,而不是实际占用的内存量。


        但是,一个集合中的元素个数多,并不一定占用的内存就多。因为,有可能每个元素占用的内存很小,这样的话,即使元素个数有很多,总内存开销也不大

3.2、SCAN命令

        使用 SCAN 命令对数据库扫描,然后用 TYPE 命令获取返回的每一个 key 的类型。

        对于 String 类型,可以直接使用 STRLEN 命令获取字符串的长度,也就是占用的内存空间字节数。

        对于集合类型来说,有两种方法可以获得它占用的内存大小:

1、如果能够预先从业务层知道集合元素的平均大小,那么,可以使用下面的命令获取集合元素的个数,然后乘以集合元素的平均大小,这样就能获得集合占用的内存大小了。List 类型:LLEN 命令;Hash 类型:HLEN 命令;Set 类型:SCARD 命令;Sorted Set 类型:ZCARD 命令;


2、如果不能提前知道写入集合的元素大小,可以使用 MEMORY USAGE 命令(需要 Redis 4.0 及以上版本),查询一个键值对占用的内存空间。

示例如下:

public List<Map.Entry<String, Long>> findBigKeys(int threshold) {List<Map.Entry<String, Long>> bigKeys = new ArrayList<>();Cursor<byte[]> cursor = redisTemplate.execute((RedisCallback<Cursor<byte[]>>) connection -> connection.scan(ScanOptions.scanOptions().count(100).build()));while (cursor.hasNext()) {byte[] keyBytes = cursor.next();String key = new String(keyBytes);DataType type = redisTemplate.type(key);long size = 0;switch (type) {case STRING:size = redisTemplate.opsForValue().size(key);break;case HASH:size = redisTemplate.opsForHash().size(key);break;// 其他类型处理...}if (size > threshold) {bigKeys.add(new AbstractMap.SimpleEntry<>(key, size));}}return bigKeys;
}

3.3、使用 RdbTools

        使用 RdbTools 第三方开源工具,可以用来解析 Redis 快照(RDB)文件,找到其中的大 key。

比如,下面这条命令,将大于 10 kb 的 key 输出到一个表格文件。

rdb dump.rdb -c memory --bytes 10240 -f redis.csv# 使用rdb-tools分析
rdb -c memory dump.rdb --bytes 10240 > bigkeys.csv# 输出示例
database,type,key,size_in_bytes,encoding,num_elements,len_largest_element
0,hash,user:1024:tags,1048576,hashtable,50000,128

4、优化方案

示例如下:

4.1、合理设计key

        分析为何会产生大key,并根据业务场景考虑是否可以通过更好的设计来避免大key的产生


4.2、拆分大key

        如果大key是不可避免的,尝试将其拆分成更多的小key来分散数据。例如,对于列表、集合和有序集合,可以通过散列(hashing)某个属性,把它们分散到不同的小key中。
对于哈希表,可以使用一致性哈希等算法将大哈希表拆分成多个小哈希表。


4.3、设置合理的过期时间

        为每个key设置过期时间,并设置合理的过期时间,以便在数据失效后自动清理,避免长时间累积的大key问题。


4.4、启用内存淘汰策略

        启用Redis的内存淘汰策略,如LRU(Least Recently Used,最近最少使用)或者LFU,以便在内存不足时自动淘汰最近最少使用的数据,防止大key长时间占用内存。


4.5、使用数据压缩

        对于String类型的大key,可以使用压缩算法(如LZF、QUICKLZ、GZIP等)减少value的大小。


4.6.、渐进式删除

        如果要删除大key,为了避免一次性删除所带来的长时间阻塞,可以使用Redis的HSCAN、SSCAN、ZSCAN和SCAN命令,配合DEL命令对大key进行渐进式删除


4.7、数据迁移

        如果单个Redis实例无法处理大key问题,可以考虑将数据迁移到使用集群,以此来分散负载和存储。


4.8、优化数据结构

        优化数据结构可能是处理大key最有效的方法。如果不必使用哈希表、列表、集合或有序集合的全部特性,可以考虑使用更简单的数据结构来替代


4.9、监控与预防

        定期监控Redis实例的内存使用情况和各种key的大小,能够帮助及时发现并处理大key问题
在开发和部署Redis系统时充分考虑大key问题,并采取相应的措施来预防和避免这些问题的出现。


总结

        通过全流程的预防、检测、处理体系建设,结合智能化的监控预警,可有效应对 BigKey 挑战,保障 Redis 高性能服务能力。


参考文章:

1、Redis大Key问题全解析:从原理到实战的深度解决方案_redis bigkey-CSDN博客https://blog.csdn.net/weixin_43674738/article/details/146159699?ops_request_misc=%257B%2522request%255Fid%2522%253A%252216eea5efdf0c25c283e92712b23989c5%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=16eea5efdf0c25c283e92712b23989c5&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-1-146159699-null-null.142^v102^pc_search_result_base1&utm_term=redis%20bigkey&spm=1018.2226.3001.4187

2、Redis中的BigKey_redis bigkey-CSDN博客https://blog.csdn.net/m0_74267125/article/details/137795444?ops_request_misc=%257B%2522request%255Fid%2522%253A%252216eea5efdf0c25c283e92712b23989c5%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=16eea5efdf0c25c283e92712b23989c5&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-2-137795444-null-null.142^v102^pc_search_result_base1&utm_term=redis%20bigkey&spm=1018.2226.3001.4187

3、Redis的热key以及Big(大)key是什么?如何解决Redis的热key以及Big(大)key问题?_redis大key和热key问题及处理-CSDN博客https://blog.csdn.net/Heyi3416/article/details/141223393?ops_request_misc=&request_id=&biz_id=102&utm_term=redis%20bigkey&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-2-141223393.142^v102^pc_search_result_base1&spm=1018.2226.3001.4187

http://www.xdnf.cn/news/1066339.html

相关文章:

  • 安全再升级! 正也科技通过信息安全等级保护三级备案
  • 七八章习题测试
  • 高级版 Web Worker 封装(含 WorkerPool 调度池 + 超时控制)
  • 本地文件深度交互新玩法:Obsidian Copilot的深度开发
  • 能耗管理新革命:物联网实现能源高效利用
  • 小学期前端三件套学习(更新中)
  • 开启游戏新时代:神经网络渲染技术实现重大跨越
  • 【Torch】nn.GRU算法详解
  • 前端跨域解决方案(7):Node中间件
  • 容器技术入门与Docker环境部署指南
  • asp.net core Razor动态语言编程代替asp.net .aspx更高级吗?
  • 如何在 Vue 应用中嵌入 ONLYOFFICE 编辑器
  • LED-Merging: 无需训练的模型合并框架,兼顾LLM安全和性能!!
  • WebSocket长连接在小程序中的实践:消息推送与断线重连机制设计
  • 运维打铁: Windows 服务器基础运维要点解析
  • 详解HarmonyOS NEXT仓颉开发语言中的全局弹窗
  • AI编程再突破,文心快码发布行业首个多模态、多智能体协同AI IDE
  • vue3整合element-plus
  • WebSocket快速入门
  • 卓易通是什么
  • 深度学习:PyTorch卷积神经网络(CNN)之图像入门
  • 【软考高级系统架构论文】论企业集成平台的理解与应用
  • Spring Boot 使用 ElasticSearch
  • 大数据时代UI前端的变革:从静态展示到动态交互
  • ISCSI存储
  • FreeRTOS 介绍、使用方法及应用场景
  • RabbitMQ从入门到实践:消息队列核心原理与典型应用场景
  • 跨域视角下强化学习重塑大模型推理:GURU框架与多领域推理新突破
  • 【论文阅读笔记】TransparentGS:当高斯溅射学会“看穿”玻璃,如何攻克透明物体重建难题?
  • 【破局痛点,赋能未来】领码 SPARK:铸就企业业务永续进化的智慧引擎—— 深度剖析持续演进之道,引领数字化新范式