前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何优雅的删除一个超过3000多万成员且内存占用超过1.8G的bigkey?

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

原创
作者头像
用户10842762
修改2023-12-22 17:45:08
3311
修改2023-12-22 17:45:08
举报
文章被收录于专栏:MySQL_DBAMySQL_DBA

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实例容量。

技术回放

代码语言:python
复制
redis_version:4.0.14,支持unlink,异步操作。
Redis 4.0及之后版本:可以通过UNLINK命令安全地删除大key甚至特大key,
该命令能够以非阻塞的方式,逐步地清理传入的key)

127.0.0.1:6379[3]> info Keyspace
# Keyspace  整个实例只有一个key,在db3  这是测试环境,生产推荐使用db0
db3:keys=1,expires=0,avg_ttl=0

127.0.0.1:6379> select 3   
OK

127.0.0.1:6379[3]> SCAN 0
1) "0"
2) 1) "hash_bigkey_test"

127.0.0.1:6379[3]> type  hash_bigkey_test
hash
 
127.0.0.1:6379[3]> ttl hash_bigkey_test
(integer) -1   表示为设置过期时间

127.0.0.1:6379[3]> HLEN hash_bigkey_test
(integer) 31414065   hash_bigkey_test成员数量为3000多万

127.0.0.1:6379[3]> info memory
# Memory
used_memory:2028479472
used_memory_human:1.89G  hash_bigkey_test 占用内存为1.89G,是非常的大了
used_memory_rss:2076168192
used_memory_rss_human:1.93G   

拓展:我喜欢在slave上做bgsave,然后分析bigkey;
rdb.py可以分析出成员的个数、过期时间、占用内存和类型等

/root/rdbtools-0.1.15/rdbtools/cli/rdb.py -c utf-8  -c memory dump.rdb  > dump1221.log  --查找bigkey的一种方法,优点:从库执行,对线上服务影响小;缺点:时效性差,RDB文件较大时耗时较长。
more  dump1221.log
database,type,key,size_in_bytes,encoding,num_elements,len_largest_element,expiry
3,hash,hash_bigkey_test,2161840836,hashtable,31414065,13,

你猜一下3000多万的key,占用内存为1.89G的bigkey,
通过del删除,需要多少时间呢?
       

执行 time redis-cli -p 6379 -a 密码 -n 3 del hash_bigkey_test 
(integer) 1
real    0m26.527s  ---答案是26秒 (我是用空闲的物理机做测试的,如果规格是:CPU 2C 内存 4GB,阻塞时间会更长))
user    0m0.002s
sys     0m0.000s

127.0.0.1:6379> SLOWLOG get  ---查询慢日志
 1) 1) (integer) 10   ---slow log唯一的id,重启后会被重置
    2) (integer) 1703158202  ---以unix时间戳表示的日志记录时间  2023-12-21 19:30:02
    3) (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 -n 20 redis_ping.log   ---输出响应结果和时间
ping  执行时间 2023-12-21 19:29:35.931984: 返回结果:pong, 耗时: 0:00:00.000152
ping  执行时间 2023-12-21 19:29:35.932159: 返回结果:pong, 耗时: 0:00:00.000164
ping  执行时间 2023-12-21 19:29:35.932321: 返回结果:pong, 耗时: 0:00:00.000151
ping  执行时间 2023-12-21 19:29:35.932483: 返回结果:pong, 耗时: 0:00:00.000151
ping  执行时间 2023-12-21 19:30:02.460248: 返回结果:pong, 耗时: 0:00:26.527754   ---ping命令发送后,pong返回时间隔了26秒,说明Redis阻塞了26秒
ping  执行时间 2023-12-21 19:30:02.463208: 返回结果:pong, 耗时: 0:00:00.002367
ping  执行时间 2023-12-21 19:30:02.463781: 返回结果:pong, 耗时: 0:00:00.000479


优雅的删除hash_bigkey_test这个key:
1.和业务方项目组沟通,确认hash_bigkey_test可以删除。(做好沟通,很重要)
2.将key改名字:RENAME hash_bigkey_test hash_bigkey_test_20231221。
(key改名后,保留一段时间,二次确认无异常访问)
3.业务低峰期,渐进式遍历hash_bigkey_test_20231221,每次取出100个成员,
然后删除,直到全部删除。

nohup  python hdel_big_key.py >> hdel_big_key.log &

more hdel_big_key.log
Key: field1633170, Value: value1633170
Response: 1, Execution Time: 0.000195026397705 seconds
Key: field87462, Value: value87462
Response: 1, Execution Time: 0.000221014022827 seconds
Key: field818659, Value: value818659
Response: 1, Execution Time: 0.000204086303711 seconds

优雅的删除hash_bigkey_test hdel_big_key.py 代码如下:

#!/usr/bin/python
# coding=utf-8
import redis
import sys
import time

db_host = "110.110.110.110"
db_port = 6379
pwd = "密码"
r = redis.StrictRedis(host=db_host, port=db_port, password=pwd, db=3)

cursor = 0

while True:
    # 使用 HSCAN 命令获取 100 个成员及下一个游标位置
    result = r.hscan("hash_bigkey_test_20231221", cursor, count=100)

    # 获取返回结果中的成员和下一个游标位置
    elements = result[1]
    cursor = result[0]

    # 处理获取的成员
    for key, value in elements.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_time
        print("Response: {}, Execution Time: {} seconds".format(response, execution_time))

    # 如果游标为 0,表示已经遍历完所有成员,结束循环
    if cursor == 0:
        break


其他类型的key,比如list、set也可以采取渐进式遍历,并小批量的删除bigkey。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云数据库 Redis
腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档