当前位置:网站首页>How to deal with keys when Redis is large?

How to deal with keys when Redis is large?

2022-08-09 22:05:00 technical trivia

今天来聊聊,关于 Redis 大 key 的四个问题.

  • 什么是 Redis 大 key?
  • 大 key 会造成什么问题?
  • 如何找到大 key ?
  • 如何删除大 key?

什么是 Redis 大 key?

大 key 并不是指 key 的值很大,而是 key 对应的 value 很大.

一般而言,The following two cases are called large key:

  • String 类型的值大于 10 KB;
  • Hash、List、Set、ZSet The number of elements of type exceeds 5000个;

大 key 会造成什么问题?

大 key It will bring the following four effects:

  • The client timed out and blocked.由于 Redis Executing the command is a single-threaded process,Then in operation big key will be more time consuming,那么就会阻塞 Redis,From the client's perspective,There is no response for a long time.
  • cause network congestion.Get big every time key 产生的网络流量较大,如果一个 key 的大小是 1 MB,每秒访问量为 1000,Then it will be generated every second 1000MB 的流量,这对于普通千兆网卡的服务器来说是灾难性的.
  • 阻塞工作线程.如果使用 del 删除大 key 时,会阻塞工作线程,Then there is no way to process subsequent commands.
  • Uneven distribution of memory.cluster model in slot 分片均匀情况下,会出现数据和查询倾斜情况,部分有大 key 的 Redis 节点占用内存多,QPS 也会比较大.

如何找到大 key ?

1、redis-cli --bigkeys 查找大key

可以通过 redis-cli --bigkeys Command Find Large key:

redis-cli -h 127.0.0.1 -p6379 -a "password" -- bigkeys

使用的时候注意事项:

  • The best option is to execute the command on the slave node.因为主节点上执行时,会阻塞主节点;
  • 如果没有从节点,那么可以选择在 Redis 实例业务压力的低峰阶段进行扫描查询,以免影响到实例的正常运行;或者可以使用 -i 参数控制扫描间隔,避免长时间扫描降低 Redis 实例的性能.

shortcomings of this method:

  • This method can only return the largest of each type bigkey,无法得到大小排在前 N 位的 bigkey;
  • 对于集合类型来说,This method only counts the number of elements in the collection,而不是实际占用的内存量.但是,There are many elements in a set,并不一定占用的内存就多.因为,It is possible that each element occupies a small amount of memory,这样的话,即使元素个数有很多,总内存开销也不大;

2、使用 SCAN Command Find Large key

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

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

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

  • 如果能够预先从业务层知道集合元素的平均大小,那么,可以使用下面的命令获取集合元素的个数,然后乘以集合元素的平均大小,这样就能获得集合占用的内存大小了.List 类型:LLEN 命令;Hash 类型:HLEN 命令;Set 类型:SCARD 命令;Sorted Set 类型:ZCARD 命令;
  • If the size of the elements written to the collection cannot be known ahead of time,可以使用 MEMORY USAGE 命令(需要 Redis 4.0 及以上版本),查询一个键值对占用的内存空间.

3、使用 RdbTools Tools look great key

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

比如,下面这条命令,将大于 10 kb 的  key  Output to a table file.

rdb dump.rdb -c memory --bytes 10240 -f redis.csv

如何删除大 key?

删除操作的本质是要释放键值对占用的内存空间,不要小瞧内存的释放过程.

释放内存只是第一步,为了更加高效地管理内存空间,在应用程序释放内存时,操作系统需要把释放掉的内存块插入一个空闲内存块的链表,以便后续进行管理和再分配.这个过程本身需要一定时间,而且会阻塞当前释放内存的应用程序.

所以,如果一下子释放了大量内存,空闲内存块链表操作时间就会增加,相应地就会造成 Redis 主线程的阻塞,If the main thread is blocked,All other requests may time out,Timeouts are increasing,会造成 Redis 连接耗尽,Various exceptions are generated.

因此,删除大 key 这一个动作,我们要小心.具体要怎么做呢?这里给出两种方法:

  • 分批次删除
  • 异步删除(Redis 4.0版本以上)

1、分批次删除

对于删除大 Hash,使用 hscan 命令,每次获取 100 个字段,再用 hdel 命令,每次删除 1 个字段.

Python代码:

def del_large_hash():
  r = redis.StrictRedis(host= 'redis-host1', port= 6379)
    large_hash_key = "xxx"  #Large to removehash键名
    cursor =  '0'
     while cursor !=  0:
         # 使用 hscan 命令,每次获取 100 个字段
        cursor, data = r.hscan(large_hash_key, cursor=cursor, count= 100)
         for item  in data.items():
                 # 再用 hdel 命令,每次删除1个字段
                r.hdel(large_hash_key, item[ 0])

对于删除大 List,通过 ltrim 命令,每次删除少量元素.

Python代码:

def del_large_list():
  r = redis.StrictRedis(host= 'redis-host1', port= 6379)
  large_list_key =  'xxx'   #Large to removelist的键名
   while r.llen(large_list_key)> 0:
       #Only the rightmost is removed each time100个元素
      r.ltrim(large_list_key,  0,  -101) 

对于删除大 Set,使用 sscan 命令,每次扫描集合中 100 个元素,再用 srem 命令每次删除一个键.

Python代码:

def del_large_set():
  r = redis.StrictRedis(host= 'redis-host1', port= 6379)
  large_set_key =  'xxx'    # Large to removeset的键名
  cursor =  '0'
   while cursor !=  0:
     # 使用 sscan 命令,每次扫描集合中 100 个元素
    cursor, data = r.sscan(large_set_key, cursor=cursor, count= 100)
     for item  in data:
       # 再用 srem 命令每次删除一个键
      r.srem(large_size_key, item)

对于删除大 ZSet,使用 zremrangebyrank 命令,每次删除 top 100个元素.

Python代码:

def del_large_sortedset():
  r = redis.StrictRedis(host= 'large_sortedset_key', port= 6379)
  large_sortedset_key= 'xxx'
   while r.zcard(large_sortedset_key)> 0:
     # 使用 zremrangebyrank 命令,每次删除 top 100个元素
    r.zremrangebyrank(large_sortedset_key, 0, 99) 

2、异步删除

从 Redis 4.0 版本开始,可以采用异步删除法,用 unlink 命令代替 del 来删除.

这样 Redis 会将这个 key Put into an asynchronous thread for deletion,This will not block the main thread.

原网站

版权声明
本文为[technical trivia]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/221/202208091902336810.html