Redis如何删除1.2亿指定前缀的key

52次阅读
没有评论

共计 3455 个字符,预计需要花费 9 分钟才能阅读完成。

这篇文章主要为大家展示了“Redis 如何删除 1.2 亿指定前缀的 key”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让丸趣 TV 小编带领大家一起研究并学习一下“Redis 如何删除 1.2 亿指定前缀的 key”这篇文章吧。

背景

因为更换 IDC 的原因,我们需要迁移缓存到新的机房,开发同学提出老的缓存有 1.2 亿无效 (未设置过期时间) 的 key 和正常在用的业务 key,在迁移之前可以先指定前缀将 key 删除。那么问题来了, 如何快速删除 1.2 亿的 key 呢?

如何获取指定的 key

大家都知道由于 Redis 的单线程服务模式,命令  keys *  会阻塞正常的业务请求,所以肯定不行。

在这里我们利用 Redis 提供的  SCAN  功能。SCAN 命令是一个基于游标的迭代器(cursor based iterator):SCAN 命令每次被调用之后,都会向用户返回一个新的游标,用户在下次迭代时需要使用这个新游标作为 SCAN 命令的游标参数,以此来延续之前的迭代过程。

当 SCAN 命令的游标参数被设置为 0 时,服务器将开始一次新的迭代,而当服务器向用户返回值为 0 的游标时,表示迭代已结束。SCAN 的语法如下  

SCAN cursor [MATCH pattern] [COUNT count]

其中 cousor 是游标,MATCH 则支持正则匹配,我们正好可以利用此功能,比如匹配 前缀为 dba_ 的 key,COUNT 是每次获取多少个 key。

redis 127.0.0.1:6379  scan 0
1)  17 
2) 1)  key:12 
 2)  key:8 
 3)  key:4 
 4)  key:14 
 5)  key:16 
 6)  key:17 
 7)  key:15 
 8)  key:10 
 9)  key:3 
 10)  key:7 
 11)  key:1 
redis 127.0.0.1:6379  scan 17
1)  0 
2) 1)  key:5 
 2)  key:18 
 3)  key:0 
 4)  key:2 
 5)  key:19 
 6)  key:13 
 7)  key:6 
 8)  key:9 
 9)  key:11

在上面这个例子中,第一次迭代使用 0 作为游标,表示开始一次新的迭代。第二次迭代使用的是第一次迭代时返回的游标,也即是命令回复第一个元素的值 —— 17。在第二次调用 SCAN 命令时,命令返回了游标 0,这表示迭代已经结束,整个数据集(collection)已经被完整遍历过了。

从上面的示例可以看到,SCAN 命令的回复是一个包含两个元素的数组,第一个数组元素是用于进行下一次迭代的新游标,而第二个数组元素则是一个数组,这个数组中包含了所有被迭代的元素。

注意: 以 0 作为游标开始一次新的迭代,一直调用 SCAN 命令,直到命令返回游标 0,我们称这个过程为一次完整遍历(full iteration)。  我们会在后面的代码实现中利用此特点。

Python 的 redis 模块提供 scan_iter 迭代器来遍历 key,其返回的结果迭代器对象。

In [53]: ret=r.scan_iter(dba_* ,20)
In [54]: print ret

至此,我们解决了如何获取数据的问题,下面思考第二个问题。

如何执行删除

这个相对比较简单,Redis 提供 DEL  命令

127.0.0.1:6379[2]  get  dba_7 
 r06cVX9 
127.0.0.1:6379[2]  get  dba_1 
 ETX57PA 
127.0.0.1:6379[2]  del  dba_7   dba_1 
(integer) 2
127.0.0.1:6379[2]

在 redis-py 中,提供了 delete(key),delete(*key)的函数, 其中参数 *key 是多个值的列表。到这里,我们大致可以想到获取 key,然后批量删除

(mytest)? test git:(master) ? python delete_key.py
initial keys successfully,use time: 90.2497739792
normal ways end at: 68.685477972
normal ways delete numbers: 1000000

常规方式的删除 10W 个 key 耗时 68.7 秒,如果是 1.2 亿个 key 要多少时间呢?68*1000/3600=18.8 小时。能不能更快呢? 

如何提高执行速度

Redis 本身是基于 Request/Response 协议的,客户端发送一个命令,等待 Redis 应答,Redis 在接收到命令,处理后应答。其中发送命令加上返回结果的时间称为(Round Time Trip)RRT- 往返时间。如果客户端发送大量的命令给 Redis,那就是等待上一条命令应答后再执行再执行下一条命令,这中间不仅仅多了 RTT,而且还频繁的调用系统 IO,发送网络请求。

Pipeline(流水线)功能极大的改善了上面的缺点。Pipeline 能将一组 Redis 命令进行组装,然后一次性传输给 Redis,再将 Redis 执行这组命令的结果按照顺序返回给客户端。

需要注意的是 Pipeline 虽然好用,但是 Pipline 组装的命令个数不能没有限制,否则一次组装数据量过大,一方面增加客户端的等待时间,另一方面会造成网络阻塞,需要批量组装。使用 Pepline 和常规方式的性能对比如下:

代码

# encoding: utf-8

author: yangyi@youzan.com

time: 2018/3/9 下午 8:35

func:

import redis

import random

import string

import time

pool = redis.ConnectionPool(host= 127.0.0.1 , port=6379, db=2)

r = redis.Redis(connection_pool=pool)

def random_str():

 return .join(random.choice(string.ascii_letters + string.digits) for _ in range(7))

def init_keys():

 start_time = time.time()

 for i in xrange(0, 20):

 key_name = dba_ +str(i)

 value_name = random_str()

 r.set(key_name, value_name)

 print initial keys successfully,use time: , time.time() – start_time

def del_keys_without_pipe():

 start_time = time.time()

 result_length = 0

 for key in r.scan_iter(match= dba_* , count=2000):

 r.delete(key)

 result_length += 1

 print normal ways end at: , time.time() – start_time

 print normal ways delete numbers: , result_length

def del_keys_with_pipe():

 start_time = time.time()

 result_length = 0

 pipe = r.pipeline()

 for key in r.scan_iter(match= dba_* , count=5000):

 pipe.delete(key)

 result_length += 1

 if result_length % 5000 == 0:

 pipe.execute()

 pip_time = time.time()

 print use pipeline scan time , time.time() – start_time

 pipe.execute()

 print use pipeline end at: , time.time() – pip_time

 print use pipeline ways delete numbers: , result_length

def main():

 init_keys()

 del_keys_without_pipe()

 init_keys()

 del_keys_with_pipe()

if __name__ == __main__ :

 main()

以上是“Redis 如何删除 1.2 亿指定前缀的 key”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注丸趣 TV 行业资讯频道!

正文完
 
丸趣
版权声明:本站原创文章,由 丸趣 2023-07-17发表,共计3455字。
转载说明:除特殊说明外本站除技术相关以外文章皆由网络搜集发布,转载请注明出处。
评论(没有评论)