首页 科技正文

威海房地产网:Redis 的 KEYS 下令不能乱用啊

admin 科技 2020-07-30 34 0

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 客户端毗邻后可设置服务器

若何禁用危险下令

在redis.conf中,在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下令:

http://doc.redisfans.com/key/scan.html

 

参考:

https://redis.io

https://www.dazhuanlan.com/2019/12/17/5df832fd189f6

● 观点注释:对称加密、非对称加密、公钥、私钥、署名、证书

● Mybatis Generator逆向工程的使用

● XStream学习手册

● 别在 Java 代码里乱打日志了,这才是准确的打日志姿势!

● 高可用Redis服务架构剖析与搭建

,

环球UG

欢迎进入环球UG官网(UG环球):www.ugbet.us,环球UG官方网站:www.ugbet.net开放环球UG网址访问、环球UG会员注册、环球UG代理申请、环球UG电脑客户端、环球UG手机版下载等业务。

版权声明

本文仅代表作者观点,
不代表本站Sunbet的立场。
本文系作者授权发表,未经许可,不得转载。

评论