本文主要给大家展示“Redis如何删除1.2亿指定前缀的密钥”,简单易懂,条理清晰,希望能帮你解决疑惑。让边肖带领大家学习学习《Redis如何删除1.2亿指定前缀的密钥》一文。
背景
因为IDC的更换,我们需要将缓存迁移到新的机房。开发同学建议旧缓存有1.2亿个无效密钥(没有设置过期时间)和正常使用的业务密钥。在迁移之前,您可以指定前缀来删除密钥。那么问题来了,如何快速删除1.2亿个密钥?
如何获取指定的密钥
众所周知,由于Redis的单线程服务模式,订购keys *会阻塞正常的业务请求,所以肯定不行。
这里我们使用Redis提供的SCAN函数。scan命令是一个基于游标的迭代器:每次调用SCAN命令时,它都会向用户返回一个新的游标,用户需要在下一次迭代中使用这个新的游标作为SCAN命令的游标参数,以便继续上一次迭代过程。
当SCAN命令的游标参数设置为0时,服务器将开始新的迭代,当服务器向用户返回一个值为0的游标时,意味着迭代已经结束。扫描具有以下语法
扫描光标[匹配模式][计数]
Cousor是一个游标,MATCH支持常规匹配。我们可以只利用这个函数,比如匹配前缀为‘DBA _’的键,COUNT就是每次获得多少个键。
redis127.0.0.1:6379scan0
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:637917
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,这意味着迭代已经结束,整个集合已经被完全遍历。
从上面的例子可以看出,s can命令的回复是一个包含两个元素的数组,第一个数组元素是下一次迭代的新光标,第二个数组元素是一个数组,包含所有迭代的元素。
注意:以 0 作为游标开始一次新的迭代, 一直调用 SCAN 命令, 直到命令返回游标 0 , 我们称这个过程为一次完整遍历(full iteration)。.我们将在后面的代码实现中使用这个特性。
Python redis模块提供sca
n_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()
# 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”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注行业资讯频道!
内容来源网络,如有侵权,联系删除,本文地址:https://www.230890.com/zhan/129599.html