Redis的KEYS命令千万不能乱⽤
KESY 命令
时间复杂度: O(N) , 假设Redis中的键名和给定的模式的长度有限的情况下,N为数据库中key的个数。
Redis Keys 命令⽤于查所有符合给定模式 pattern 的 key
尽管这个操作的时间复杂度是 O(N), 但是常量时间相当低。例如,在⼀个普通笔记本上跑Redis,扫描100万个key只要40毫秒。
命令格式 KEYS pattern
Warning: ⽣产环境使⽤ KEYS 命令需要⾮常⼩⼼。在⼤的数据库上执⾏命令会影响性能。这个命令适合⽤来调试和特殊操作,像改变键空间keyspace布局。不要在你的代码中使⽤ KEYS 。如果你需要⼀个寻键空间中的key⼦集,考虑使⽤SCAN 或集合结构sets。
正则匹配最后一次出现的字符
⽀持的匹配模式 patterns:
h?llo 匹配 hello, hallo 和 hxllo
h*llo 匹配 hllo 和 heeeello
h[ae]llo 匹配 hello 和 hallo, 不匹配 hillo
h[^e]llo 匹配 hallo, hbllo, … 不匹配 hello
h[a-b]llo 匹配 hallo 和 hbllo
使⽤ \ 转义你想匹配的特殊字符。
背景
1、Redis是单线程的,其所有操作都是原⼦的,不会因并发产⽣数据异常
2、使⽤⾼耗时的Redis命令是很危险的,会占⽤唯⼀的⼀个线程的⼤量处理时间,导致所有的请求都被拖慢
场景
在⽣产环境中执⾏ KEYS 命令的时,因为Redis是单线程的,KEYS 命令的性能随着数据库数据的增多⽽越来越慢,使⽤KEYS 命令时会占⽤唯⼀的⼀个线程的⼤量处理时间,引发Redis阻塞并且增加Redis的CPU占⽤,导致所有的请求都被拖慢,可能造成Redis所在的服务器宕机,情况是很恶劣的,在实际⽣产运⽤的过程中请忽略掉。试想如果Redis阻塞超
过10秒,如果有集的场景,可能导致集判断Redis已经故障,从⽽进⾏故障切换。
如果所有的线程在Redis那取不到数据,情况严重时可能会导致应⽤程序出现雪崩的情况,⼀瞬间全去数据库取数据,数据库就宕机了。
其他危险命令
但凡发现时间复杂度为O(N)的命令,都要慎重,不要在⽣产上随便使⽤。例如hgetall、lrange、smembers、zrange、sinter 等命令,它们并⾮不能使⽤,但这些命令的时间复杂度都为O(N),使⽤这些命令需要明确N的值,否则也会出现缓存宕机。
1、flushdb 命令⽤于清空当前数据库中的所有 key
2、flushall 命令⽤于清空整个 Redis 服务器的数据(删除所有数据库的所有 key )
3、config 客户端连接后可配置服务器
如何禁⽤危险命令
在f中,在SECURITY这⼀项中,新增以下配置禁⽤指定命令:
rename-command FLUSHALL ""
rename-command FLUSHDB ""
rename-command CONFIG ""
rename-command KEYS ""
另外,对于flushall命令,需要设置配置⽂件中appendonly no,否则服务器是⽆法启动。
如果想要保留命令,但是不轻易使⽤,可以重命名命令来设定:
rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52
TIP:更改记录到AOF⽂件或传输到从属服务器的命令的名称可能会导致问题。
改良建议
⼀、如果有这种需求的话可以⾃⼰对键值做索引,⽐如把各种键值存到不同的set⾥⾯,分类建⽴索引,这样就可以很快的得到数据,但是这样也存在⼀个明显的缺点,就是浪费宝贵的空间,所以还是要合理考虑,当然也可以想办法,⽐如对于有规律的键值,可以存储他们的始末值等等。
⼆、针对改良keys和smembers命令也可以使⽤scan命令
SCAN 命令
Redis从2.8版本开始⽀持scan命令,可以⽤来分批次扫描Redis记录,这样肯定会导致整个查询消耗的总时间变⼤,影响服务使⽤,但不会影响redis服务卡顿,SCAN命令的基本⽤法如下:
命令格式 SCAN cursor [MATCH pattern] [COUNT count]
SCAN 命令提供三个参数,第⼀个是cursor(游标),第⼆个是要匹配的正则,第三个是单次遍历的槽位
SCAN 命令及其相关的 SSCAN 命令、 HSCAN 命令和 ZSCAN 命令都⽤于增量地迭代(incrementally iterate)⼀集元素(a collection of elements):
SCAN 命令⽤于迭代当前数据库中的数据库键。
SSCAN 命令⽤于迭代集合键中的元素。
HSCAN 命令⽤于迭代哈希键中的键值对。
ZSCAN 命令⽤于迭代有序集合中的元素(包括元素成员和元素分值)。
以上列出的四个命令都⽀持增量式迭代,它们每次执⾏都只会返回少量元素,所以这些命令可以⽤于⽣产环境,⽽不会出现像KEYS 命令、SMEMBERS 命令带来的问题 —— 当 KEYS 命令被⽤于处理⼀个⼤的数据库时,⼜或者 SMEMBERS 命令被⽤于处理⼀个⼤的集合键时,它们可能会阻塞服务器达数秒之久。
不过,增量式迭代命令也不是没有缺点的:举个例⼦,使⽤ SMEMBERS 命令可以返回集合键当前包含的所有元素,但是对于 SCAN 这类增量式迭代命令来说,因为在对键进⾏增量式迭代的过程中,键可能会被修改,所以增量式迭代命令只能对被返回的元素提供有限的保证(offer limited guarantees about the returned elements)。
因为 SCAN 、 SSCAN 、 HSCAN 和 ZSCAN 四个命令的⼯作⽅式都⾮常相似,所以接下来会⼀并介绍这四个命令,但是要记住:
SSCAN 命令、 HSCAN 命令和 ZSCAN 命令的第⼀个参数总是⼀个数据库键。
⽽ SCAN 命令则不需要在第⼀个参数提供任何数据库键 —— 因为它迭代的是当前数据库中的所有数据库键。
SCAN 命令的基本⽤法
SCAN 命令是⼀个基于游标的迭代器(cursor based iterator):SCAN 命令每次被调⽤之后,都会向⽤户返回⼀个新的游标,⽤户在下次迭代时需要使⽤这个新游标作为 SCAN 命令的游标参数,以此来延续之前的迭代过程。
当 SCAN 命令的游标参数被设置为 0 时,服务器将开始⼀次新的迭代,⽽当服务器向⽤户返回值为 0 的游标时,表⽰迭代已结束。
以下是⼀个 SCAN 命令的迭代过程⽰例:
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 命令的回复是⼀个包含两个元素的数组,第⼀个数组元素是⽤于进⾏下⼀次迭代的新游标,⽽第⼆个数组元素则是⼀个数组,这个数组中包含了所有被迭代的元素。
在第⼆次调⽤ SCAN 命令时,命令返回了游标 0 ,这表⽰迭代已经结束,整个数据集(collection)已经被完整遍历过了。
以 0 作为游标开始⼀次新的迭代,⼀直调⽤ SCAN 命令,直到命令返回游标 0 ,我们称这个过程为⼀次完整遍历(full iteration)。
SCAN 命令的保证(guarantees)
SCAN 命令,以及其他增量式迭代命令,在进⾏完整遍历的情况下可以为⽤户带来以下保证:从完整遍历开始直到完整遍历结束期间,⼀直存在于数据集内的所有元素都会被完整遍历返回;这意味着,如果有⼀个元素,它从遍历开始直到遍历结束期间都存在于被遍历的数据集当中,那么 SCAN 命令总会在某次迭代中将这个元素返回给⽤户。
然⽽因为增量式命令仅仅使⽤游标来记录迭代状态,所以这些命令带有以下缺点:
同⼀个元素可能会被返回多次。处理重复元素的⼯作交由应⽤程序负责,⽐如说,可以考虑将迭代返回的元素仅仅⽤于可以安全地重复执⾏多次的操作上。
如果⼀个元素是在迭代过程中被添加到数据集的,⼜或者是在迭代过程中从数据集中被删除的,那么这个元素可能会被返回,也可能不会,这是未定义的(undefined)。
SCAN 命令每次执⾏返回的元素数量
增量式迭代命令并不保证每次执⾏都返回某个给定数量的元素。
增量式命令甚⾄可能会返回零个元素,但只要命令返回的游标不是 0 ,应⽤程序就不应该将迭代视作结束。
不过命令返回的元素数量总是符合⼀定规则的,在实际中:
对于⼀个⼤数据集来说,增量式迭代命令每次最多可能会返回数⼗个元素
⽽对于⼀个⾜够⼩的数据集来说,如果这个数据集的底层表⽰为编码数据结构(encoded data structure,适⽤于是⼩集合键、⼩哈希键和⼩有序集合键),那么增量迭代命令将在⼀次调⽤中返回数据集中的所有元素。
最后,⽤户可以通过增量式迭代命令提供的 COUNT 选项来指定每次迭代返回元素的最⼤值。
COUNT 选项
虽然增量式迭代命令不保证每次迭代所返回的元素数量,但我们可以使⽤ COUNT 选项,对命令的⾏为进⾏⼀定程度上的调整。
基本上, COUNT 选项的作⽤就是让⽤户告知迭代命令,在每次迭代中应该从数据集⾥返回多少元素。
虽然 COUNT 选项只是对增量式迭代命令的⼀种提⽰(hint),但是在⼤多数情况下,这种提⽰都是有效的。
COUNT 参数的默认值为 10 。
在迭代⼀个⾜够⼤的、由哈希表实现的数据库、集合键、哈希键或者有序集合键时,如果⽤户没有使⽤ MATCH 选项,那么命令返回的元素数量通常和 COUNT 选项指定的⼀样,或者⽐ COUNT 选项指定的数量稍多⼀些。
在迭代⼀个编码为整数集合(intset,⼀个只由整数值构成的⼩集合)、或者编码为压缩列表(ziplist,由不同值构成的⼀个⼩哈希或者⼀个⼩有序集合)时,增量式迭代命令通常会⽆视 COUNT 选项指定的值,在第⼀次迭代就将数据集包含的所有元素都返回给⽤户。
并⾮每次迭代都要使⽤相同的 COUNT 值
⽤户可以在每次迭代中按⾃⼰的需要随意改变 COUNT 值,只要记得将上次迭代返回的游标⽤到下次迭代⾥⾯就
可以了
MATCH 选项
和 KEYS 命令⼀样,增量式迭代命令也可以通过提供⼀个 glob 风格的模式参数,让命令只返回和给定模式相匹配的元素,这⼀点可以通过在执⾏增量式迭代命令时,通过给定 MATCH参数来实现。
以下是⼀个使⽤ MATCH 选项进⾏迭代的⽰例:
redis 127.0.0.1:6379> sadd myset 1 2 3 foo foobar feelsgood
(integer) 6
redis 127.0.0.1:6379> sscan myset 0 match f*
1) "0"
2) 1) "foo"
2) "feelsgood"
3) "foobar"
需要注意的是,对元素的模式匹配⼯作是在命令从数据集中取出元素之后,向客户端返回元素之前的这段时间内进⾏的,所以如果被迭代的数据集中只有少量元素和模式相匹配,那么迭代命令或许会在多次执⾏中都不返回任何元素。
以下是这种情况的⼀个例⼦:
redis 127.0.0.1:6379> scan 0 MATCH *11*
1) "288"
2) 1) "key:911"
redis 127.0.0.1:6379> scan 288 MATCH *11*
1) "224"
2) (empty list or set)
redis 127.0.0.1:6379> scan 224 MATCH *11*
1) "80"
2) (empty list or set)
redis 127.0.0.1:6379> scan 80 MATCH *11*
1) "176"
2) (empty list or set)
redis 127.0.0.1:6379> scan 176 MATCH *11* COUNT 1000
1) "0"
2) 1) "key:611"
2) "key:711"
3) "key:118"
4) "key:117"
5) "key:311"
6) "key:112"
7) "key:111"
8) "key:110"
9) "key:113"
10) "key:211"
11) "key:411"
12) "key:115"
13) "key:116"
14) "key:114"
15) "key:119"
16) "key:811"
17) "key:511"
18) "key:11"
如你所见,以上的⼤部分迭代都不返回任何元素。
在最后⼀次迭代,我们通过将 COUNT 选项的参数设置为 1000 ,强制命令为本次迭代扫描更多元素,从⽽使得命令返回的元素也变多了。
并发执⾏多个迭代
在同⼀时间,可以有任意多个客户端对同⼀数据集进⾏迭代,客户端每次执⾏迭代都需要传⼊⼀个游标,并在迭代执⾏之后获得⼀个新的游标,⽽这个游标就包含了迭代的所有状态,因此,服务器⽆须为迭代记录任何状态。
中途停⽌迭代
因为迭代的所有状态都保存在游标⾥⾯,⽽服务器⽆须为迭代保存任何状态,所以客户端可以在中途停⽌⼀个迭代,⽽⽆须对服务器进⾏任何通知。
即使有任意数量的迭代在中途停⽌,也不会产⽣任何问题。
使⽤错误的游标进⾏增量式迭代
使⽤间断的(broken)、负数、超出范围或者其他⾮正常的游标来执⾏增量式迭代并不会造成服务器崩溃,但可能会让命令产⽣未定义的⾏为。
未定义⾏为指的是,增量式命令对返回值所做的保证可能会不再为真。
只有两种游标是合法的:
1、在开始⼀个新的迭代时,游标必须为 0 。
2、增量式迭代命令在执⾏之后返回的,⽤于延续(continue)迭代过程的游标。
迭代终结的保证
增量式迭代命令所使⽤的算法只保证在数据集的⼤⼩有界(bounded)的情况下,迭代才会停⽌,换句话说,如果被迭代数据集的⼤⼩不断地增长的话,增量式迭代命令可能永远也⽆法完成⼀次完整迭代。
从直觉上可以看出,当⼀个数据集不断地变⼤时,想要访问这个数据集中的所有元素就需要做越来越多的⼯作,能否结束⼀个迭代取决于⽤户执⾏迭代的速度是否⽐数据集增长的速度更快。
时间复杂度:
增量式迭代命令每次执⾏的复杂度为 O(1) ,对数据集进⾏⼀次完整迭代的复杂度为 O(N) ,其中 N 为数据集中的元素数量。
返回值:
SCAN 命令、 SSCAN 命令、 HSCAN 命令和 ZSCAN 命令都返回⼀个包含两个元素的 multi-bulk 回复:回复的第⼀个元素是字符串表⽰的⽆符号 64 位整数(游标),回复的第⼆个元素是另⼀个 multi-bulk 回复,这个 multi-bulk 回复包含了本次被迭代的元素。
SCAN 命令返回的每个元素都是⼀个数据库键。
SSCAN 命令返回的每个元素都是⼀个集合成员。
HSCAN 命令返回的每个元素都是⼀个键值对,⼀个键值对由⼀个键和⼀个值组成。
ZSCAN 命令返回的每个元素都是⼀个有序集合元素,⼀个有序集合元素由⼀个成员(member)和⼀个分值(score)组成。更多⽤法可查看Redis SCAN命令:
参考:
总结
到此这篇关于Redis的KEYS 命令千万不能乱⽤的⽂章就介绍到这了,更多相关Redis的KEYS 命令内容请搜索以前的⽂章或继续浏览下⾯的相关⽂章希望⼤家以后多多⽀持!

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。