如何优雅的删除一个超过3000多万成员且内存占用超过1.8G的bigkey?

本文涉及的产品
云原生内存数据库 Tair,内存型 2GB
云数据库 Redis 版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: 通过渐进式小批量删除Redis hash类型的bigkey。

Redis被广泛的应用,得益于它支持高性能访问(微秒级)。作为一个DBA,经常需要去维护bigkey。如果现在业务方需要你去删除一个hash类型的key,且这个key有3000多万个成员,内存占用超过1.8G。如何优雅的删除这个bigkey呢?下面让我来简单的介绍一下。


bigkey定义:  

key本身的数据量过大:一个string类型的key,它的值为5 MB。key中的成员数过多:一个zset类型的key,它的成员数量为10万个。key中成员的数据量过大:一个hash类型的key,它的成员数量虽然只有1000个但这些成员的value(值)总大小为100 MB。    


bigkey危害:    

长尾延迟,客户端执行命令的时长变慢。   对bigkey执行读请求,会使Redis实例的带宽使用率被占满,导致自身服务变慢,同时易波及相关的服务。  

对bigkey执行删除操作,易造成主库较长时间的阻塞,进而可能引发同步中断或主从切换。  

Redis内存达到maxmemory参数定义的上限引发操作阻塞或重要的Key被逐出,甚至引发内存溢出(Out Of Memory)。  

集群架构下,容易导致数据分片的内存资源倾斜、CPU使用率倾斜、带宽倾斜。


案例描述:

生产环境,DBA错误的使用了DEL命令删除一个bigkey,导致Redis出现阻塞。


案例警示:

1.合理制度规范(风险操作需要审核,多沟通和多确认),能有效的减少故障。

2.避免使用bigkey。

3.控制Redis实例容量。


技术回放


redis_version:4.0.14,支持unlink,异步操作。Redis4.0及之后版本:可以通过UNLINK命令安全地删除大Key甚至特大Key,该命令能够以非阻塞的方式,逐步地清理传入的Key)127.0.0.1:6379[3]>infoKeyspace# Keyspace  整个实例只有一个key,在db3  这是测试环境,生产推荐使用db0db3:keys=1,expires=0,avg_ttl=0127.0.0.1:6379>select3OK127.0.0.1:6379[3]>SCAN01)"0"2)1)"hash_bigkey_test"127.0.0.1:6379[3]>typehash_bigkey_testhash127.0.0.1:6379[3]>ttlhash_bigkey_test(integer) -1表示为设置过期时间127.0.0.1:6379[3]>HLENhash_bigkey_test(integer) 31414065hash_bigkey_test成员数量为3000多万127.0.0.1:6379[3]>infomemory# Memoryused_memory:2028479472used_memory_human:1.89Ghash_bigkey_test占用内存为1.89G,是非常的大了used_memory_rss:2076168192used_memory_rss_human:1.93G拓展:我喜欢在slave上做bgsave,然后分析bigkey;rdb.py可以分析出成员的个数、过期时间、占用内存和类型等/root/rdbtools-0.1.15/rdbtools/cli/rdb.py-cutf-8-cmemorydump.rdb>dump1221.log--查找bigkey的一种方法,优点:从库执行,对线上服务影响小;缺点:时效性差,RDB文件较大时耗时较长。moredump1221.logdatabase,type,key,size_in_bytes,encoding,num_elements,len_largest_element,expiry3,hash,hash_bigkey_test,2161840836,hashtable,31414065,13,
你猜一下3000多万的key,占用内存为1.89G的bigkey,通过del删除,需要多少时间呢?执行timeredis-cli-p6379-a密码-n3delhash_bigkey_test(integer) 1real0m26.527s---答案是26秒(我是用空闲的物理机做测试的,如果规格是:CPU2C内存4GB,阻塞时间会更长)user0m0.002ssys0m0.000s127.0.0.1:6379>SLOWLOGget---查询慢日志1)1) (integer) 10---slowlog唯一的id,重启后会被重置2) (integer) 1703158202---以unix时间戳表示的日志记录时间2023-12-2119:30:023) (integer) 26524988---命令执行时间,单位微秒,26秒4)1)"del"2)"hash_bigkey_test"5)"127.0.0.1:18390"6)""证明Redis确实阻塞了26秒,如下:编写一个脚本redis_ping.py,不断对Redis发起ping操作,返回的结果输入到redis_ping.log中tailf-n20redis_ping.log---输出响应结果和时间ping执行时间2023-12-2119:29:35.931984: 返回结果:pong, 耗时: 0:00:00.000152ping执行时间2023-12-2119:29:35.932159: 返回结果:pong, 耗时: 0:00:00.000164ping执行时间2023-12-2119:29:35.932321: 返回结果:pong, 耗时: 0:00:00.000151ping执行时间2023-12-2119:29:35.932483: 返回结果:pong, 耗时: 0:00:00.000151ping执行时间2023-12-2119:30:02.460248: 返回结果:pong, 耗时: 0:00:26.527754---ping命令发送后,pong返回时间隔了26秒,说明Redis阻塞了26秒ping执行时间2023-12-2119:30:02.463208: 返回结果:pong, 耗时: 0:00:00.002367ping执行时间2023-12-2119:30:02.463781: 返回结果:pong, 耗时: 0:00:00.000479优雅的删除hash_bigkey_test这个key:1.和业务方项目组沟通,确认hash_bigkey_test可以删除。(做好沟通,很重要)2.将key改名字:RENAMEhash_bigkey_testhash_bigkey_test_20231221。(key改名后,保留一段时间,二次确认无异常访问)3.业务低峰期,渐进式遍历hash_bigkey_test_20231221,每次取出100个成员,然后删除,直到全部删除。nohuppythonhdel_big_key.py>>hdel_big_key.log&morehdel_big_key.logKey: field1633170, Value: value1633170Response: 1, ExecutionTime: 0.000195026397705secondsKey: field87462, Value: value87462Response: 1, ExecutionTime: 0.000221014022827secondsKey: field818659, Value: value818659Response: 1, ExecutionTime: 0.000204086303711seconds优雅的删除hash_bigkey_testhdel_big_key.py代码如下:#!/usr/bin/python# coding=utf-8importredisimportsysimporttimedb_host="110.110.110.110"db_port=6379pwd="密码"r=redis.StrictRedis(host=db_host, port=db_port, password=pwd, db=3)
cursor=0whileTrue:
# 使用 HSCAN 命令获取 100 个成员及下一个游标位置result=r.hscan("hash_bigkey_test_20231221", cursor, count=100)
# 获取返回结果中的成员和下一个游标位置elements=result[1]
cursor=result[0]
# 处理获取的成员forkey, valueinelements.items():
# 输出成员print("Key: {}, Value: {}".format(key, value))
# 测量删除操作的执行时间start_time=time.time()
response=r.hdel("hash_bigkey_test_20231221", key)
end_time=time.time()
# 输出删除操作的执行时间execution_time=end_time-start_timeprint("Response: {}, Execution Time: {} seconds".format(response, execution_time))
# 如果游标为 0,表示已经遍历完所有成员,结束循环ifcursor==0:
break其他类型的key,比如list、set也可以采取渐进式遍历,并小批量的删除bigkey。分享数据库故障处理的微信公众号:MySQL_DBA,欢迎关注,谢谢!
相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
1月前
|
编译器 C++
virtual类的使用方法问题之C++类中的非静态数据成员是进行内存对齐的如何解决
virtual类的使用方法问题之C++类中的非静态数据成员是进行内存对齐的如何解决
|
3月前
|
存储 Python
Python成员属性的内存特性与底层内存优化方案
这篇博客主要分享一下python成员属性的内存特性,也就是python底层节约内存的优化方案
|
算法 Java
为什么设置-Xmx4g但是java进程内存占用达到8g?
为什么设置-Xmx4g但是java进程内存占用达到8g?
1841 0
为什么设置-Xmx4g但是java进程内存占用达到8g?
|
11月前
|
算法
实测Hutool的雪花算法8G内存跑到7600万条OOM
实测Hutool的雪花算法8G内存跑到7600万条OOM
|
编译器 C语言 Swift
05-📝C++核心语法|面向对象3【 继承和派生、多态、静态成员、const成员、引用类型成员、VS的内存窗口】
复习`C++核心语法`,且适当进行汇编探索底层实现原理,进一步夯实基础,为以后的`底层开发`、`音视频开发`、`跨平台开发`、`算法`等方向的进一步学习埋下伏笔。
05-📝C++核心语法|面向对象3【 继承和派生、多态、静态成员、const成员、引用类型成员、VS的内存窗口】
|
存储 Java 编译器
给32位系统装8g内存条能用吗?为什么?
给32位系统装8g内存条能用吗?为什么?
218 0
|
C++
C++ 静态数据成员与静态函数成员实例 友元函数实例 动态分配内存实例
C++ 静态数据成员与静态函数成员实例 友元函数实例 动态分配内存实例
74 0
结构体、联合体的成员内存对齐的情况
结构体、联合体的成员内存对齐的情况
108 0
结构体、联合体的成员内存对齐的情况
|
运维 监控 Java
5G最大堆内存的JVM进程占满云主机8G内存该何去何从(一)
一步一步的将理论用于实战,JVM,原来如此深不见底~
320 0
5G最大堆内存的JVM进程占满云主机8G内存该何去何从(一)
|
存储 编解码 弹性计算
阿里云4核8G云服务器CPU内存网络性能及价格表收费标准
阿里云4核8G云服务器CPU内存网络性能及价格表收费标准
639 0
阿里云4核8G云服务器CPU内存网络性能及价格表收费标准

热门文章

最新文章