您的位置:澳门皇冠金沙网站 > 办公软件 > Redis获取某个前缀的key脚本实例澳门皇冠金沙网站

Redis获取某个前缀的key脚本实例澳门皇冠金沙网站

2020-04-15 14:36

1.背景

前言

在平时的维护中,经常会遇到要统计某个前缀的key有多少,在请求比较多的redis中,keys * 会直接导致阻塞。可以采用scan的方式进行增量迭代,查询使用pipeline减少交互,提高效率。

熟悉Redis的人都知道,它是单线程的。因此在使用一些时间复杂度为O(N)的命令时要非常谨慎。可能一不小心就会阻塞进程,导致Redis出现卡顿。

2.scan命令的优缺点

有时,我们需要针对符合条件的一部分命令进行操作,比如删除以test_开头的key。那么怎么获取到这些key呢?在Redis2.8版本之前,我们可以使用keys命令按照正则匹配得到我们需要的key。但是这个命令有两个缺点:

SCAN命令的有SCAN,SSCAN,HSCAN,ZSCAN。

没有limit,我们只能一次性获取所有符合条件的key,如果结果有上百万条,那么等待你的就是“无穷无尽”的字符串输出。 keys命令是遍历算法,时间复杂度是O(N)。如我们刚才所说,这个命令非常容易导致Redis服务卡顿。因此,我们要尽量避免在生产环境使用该命令。

SCAN的话就是遍历所有的keys

在满足需求和存在造成Redis卡顿之间究竟要如何选择呢?面对这个两难的抉择,Redis在2.8版本给我们提供了解决办法——scan命令。

其他的SCAN命令的话是SCAN选中的集合。

相比于keys命令,scan命令有两个比较明显的优势:

SCAN命令是增量的循环,每次调用只会返回一小部分的元素。所以不会有KEYS命令的坑。

scan命令的时间复杂度虽然也是O(N),但它是分次进行的,不会阻塞线程。 scan命令提供了limit参数,可以控制每次返回结果的最大条数。

SCAN命令返回的是一个游标,从0开始遍历,到0结束遍历。

这两个优势就帮助我们解决了上面的难题,不过scan命令也并不是完美的,它返回的结果有可能重复,因此需要客户端去重。至于为什么会重复,相信你看完本文之后就会有答案了。

scan 01) "655"2) 1) "test1" 2) "test2"

关于scan命令的基本用法,可以参看Redis命令详解:Keys一文中关于SCAN命令的介绍。

返回值一个array,一个是下次循环的cursorId,一个是元素数组。SCAN命令不能保证每次返回的值都是有序的,另外同一个key有可能返回多次,不做区分,需要应用程序去处理。

SCAN 命令

另外SCAN命令可以指定COUNT,默认是10。但是这个并不是指定多少,就能返回多少,这只是一个提示,并不能保证一定返回这么多条。

SCAN命令的有SCAN,SSCAN,HSCAN,ZSCAN。

优点:

SCAN的话就是遍历所有的keys

提供键空间的遍历操作,支持游标,复杂度O(1), 整体遍历一遍只需要O(N); 提供结果模式匹配; 支持一次返回的数据条数设置,但仅仅是个hints,有时候返回的会多; 弱状态,所有状态只需要客户端需要维护一个游标;

其他的SCAN命令的话是SCAN选中的集合。

缺点:

SCAN命令是增量的循环,每次调用只会返回一小部分的元素。所以不会有KEYS命令的坑。

无法提供完整的快照遍历,也就是中间如果有数据修改,可能有些涉及改动的数据遍历不到; 每次返回的数据条数不一定,极度依赖内部实现; 返回的数据可能有重复,应用层必须能够处理重入逻辑;

SCAN命令返回的是一个游标,从0开始遍历,到0结束遍历。

3. python脚本的实现

今天我们主要从底层的结构和源码的角度来讨论scan是如何工作的。

python中有一个封装的函数scan_iter--查看所有元素--迭代器

Redis的结构

脚本内容:

Redis使用了Hash表作为底层实现,原因不外乎高效且实现简单。说到Hash表,很多Java程序员第一反应就是HashMap。没错,Redis底层key的存储结构就是类似于HashMap那样数组+链表的结构。其中第一维的数组大小为2n(n=0)。每次扩容数组长度扩大一倍。

#!/usr/bin/env python# -*- coding: UTF-8 -*- #作用:统计某个前缀key的个数,并将其输入到文件#使用方法:python scan_redis.py apus* 100__author__ = "lcl" import sysimport redis import os pool=redis.ConnectionPool(host='192.168.225.128',port=6379,db=0) r = redis.StrictRedis(connection_pool=pool) #扫描匹配值,通过sys.argv传参match = sys.argv[1]#每次匹配数量count = sys.argv[2]#print match#print count#总数量total = 0#扫描到的key输出到文件path = os.getcwd()#扫描到的key输出的文件txt = path+"/keys.txt"f = open(txt,"w")for key in r.scan_iter(match = match,count = count):# f.write("%s %s" % (key,"n")) f.write(key+"n") total = total+1f.closeprint "匹配: %s 的数量为:%d " % (match,total)

scan命令就是对这个一维数组进行遍历。每次返回的游标值也都是这个数组的索引。limit参数表示遍历多少个数组的元素,将这些元素下挂接的符合条件的结果都返回。因为每个元素下挂接的链表大小不同,所以每次返回的结果数量也就不同。

总结

SCAN的遍历顺序

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对脚本之家的支持。

关于scan命令的遍历顺序,我们可以用一个小栗子来具体看一下。

127.0.0.1:6379 keys *1) "db_number"2) "key1"3) "myKey"127.0.0.1:6379 scan 0 MATCH * COUNT 11) "2"2) 1) "db_number"127.0.0.1:6379 scan 2 MATCH * COUNT 11) "1"2) 1) "myKey"127.0.0.1:6379 scan 1 MATCH * COUNT 11) "3"2) 1) "key1"127.0.0.1:6379 scan 3 MATCH * COUNT 11) "0"2) (empty list or set)

本文由澳门皇冠金沙网站发布于办公软件,转载请注明出处:Redis获取某个前缀的key脚本实例澳门皇冠金沙网站

关键词: