当前位置: 首页 > 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

相关文章:

  • 在训练词编码模型使用mask还是自回归,在训练生成大模型采用mask还是自回归?
  • 601N1 icm45696 串口python读取及显示
  • GO 语言学习 之 代码风格
  • Towards Generalizable Diabetic Retinopathy Grading in Unseen Domains
  • 【C++】哈希表的实现(链地址法)
  • Redis哨兵模式深度解析与实战部署
  • Clickhouse原理剖析
  • Elasticsearch 搜索的流程
  • 最新发布 | “龙跃”(MindLoongGPT)大模型正式发布!龙跃而起,推动中国方案走向全球智能体前沿
  • 电脑的虚拟内存对性能影响大吗
  • [go] 垃圾回收源码解析
  • MCU双分区方案,如何优雅地获知当前运行分区?
  • 新高考需求之一
  • pyhton基础【15】函数进阶一
  • 从厨房到代码台:用做菜思维理解iOS开发 - Swift入门篇①
  • ADIOS2 介绍与使用指南
  • Vue3 + Vite + TypeScript SVG图标解决方案
  • 一款基于 React 的开源酷炫动画库
  • C指针总结复习(结合deepseek)
  • 71-Oracle Undo与Flashback管理(Guarantee设置)深度解析
  • 网站开发模板代码/站长源码
  • 网站推广视频的服务方案/电商培训内容
  • 做微信封面模板下载网站/搜索引擎营销题库和答案
  • 如何建立网站是什么/码迷seo
  • .net商城网站开发/百度seo推广
  • 淘宝联盟个人网站怎么做/seo技术快速网站排名