Redis 如何从海量数据中查询某一个 Key ?

答:一般来说,我们接触最多的是使用 keys 来进行查询,keys 使用的话也比较简单,但是在正式环境中话必须禁止使用 keys, 而是推荐使用 scan.

keys 的使用

语法:keys PATTERN:用于查找所有符合给定模式 PATTERN 的 key

keys * # 查询 redis 中所有的 key
keys h?llo # ?: 通配单个字符, 可以是 hello hallo ..., 不包含 hllo.
keys h*llo # * 通配任意多个字符, hllo hello heello heallo ... 
keys h[ae]llo # []: 通配括号内的某一个字符,可以查询出 hello hallo, 不可以查出 hllo.
keys h[^e]llo # 匹配 e 之前的字母, 可以是 hallo ... hdllo ,不包含 hello.
keys h[a-b]llo # - 相当于 or, 匹配 a 或 b, 可以是 hallo hbllo; 可以添加更多的条件,比如: keys h[a-b-c-d]llo 

# 上面的语法还可以进行相应的组合,如
keys h[a-b]*llo # 匹配 ha(任意多个字符)llo 和 hb(任意多个字符)llo

正式环境禁用keys的原因

keys 的时间复杂度为:O(N), 其中N为数据库中密钥的数目,假设数据库中的密钥名称和给定模式的长度有限。尽管其复杂的为 O(N), 但是持续的时间却很短,在一个入门级别的电脑上可以做到 40ms 扫描 100 万个数据。

但是如果是高并发的条件下,使用 keys 就会出现问题,因为 Redis 是一个单线程的数据库,每次执行命令都会对数据库进行加锁,并且 keys 命令没有分页功能,每次都会遍历整个数据库,假如 20ms 执行完一次操作,如果是百万级并发,那么 Redis 每次执行命令都会出现短暂的锁住,进而导致大量的请求被堵塞,导致其他业务不可用,进而造成 CPU 使用率高,最后造成服务器宕机。

keys 存在的问题:

  1. 没有分页功能,一次会遍历所有的数据库,并查询出所有符合条件的 key 值;但是查询的结果有用的有可能很少那就很耗资源。
  2. 尽管查询速度很快,但随着数据量的增长,查询的时间也会变得越长。

SCAN 的使用

语法:SCAN cursor [MATCH pattern] [COUNT count] [TYPE type]

SCAN是基于游标的迭代器。这意味着在每次调用该命令时,服务器都会返回相应的查询数据和一个新的游标,用户需要将该游标用作下一个调用中的游标参数。

游标设置为0时,迭代将开始,服务器返回的游标为0时,迭代将终止。

参数说明

  • cursor: 游标位置( hash 桶的索引值),整数值;从 0 开始,到 0 结束;查询的结果有可能是0个,但游标不为 0, 只要游标不为 0,就代表遍历还没有结束。
  • match pattern: 正则匹配字段 (可选)
  • count: 限定单次扫描的数量 limit hint(参考值,底层遍历的数量不一定),默认为10。其并不是查询结果返回的最大数量。比如 count 为 10000,意味着每次扫描 1w 条记录,但是有可能只有 10 条符合条件或者有 2w 条记录符合。(可选)

注意事项:

  1. 游标是一个 Hash 值,是乱序的,一个遍历从 从 0 开始,到 0 结束,意味着重新返回起点。
  2. 无需为每次迭代使用相同的 COUNT 值。只要在下一次遍历时是传入上一次的获得的游标即可,这样下一次遍历会使用上一次的 COUNT 值。
  3. scan 返回的结果可能会有重复数据,需要客户端去重
  4. 新增的数据有可能没有被遍历到
127.0.0.1:6379> scan 0 match 1* count 15
 1) "17"
 2)  1) "key:12"
     2) "key:18"
     3) "key:14"
     4) "key:14"
     5) "key:16"
     6) "key:17"
     7) "key:15"
     8) "key:10"
     9) "key:13"
    10) "key:17"
    11) "key:1"  # 扫描 15 个元素,但是只有 11 个元素符合
127.0.0.1:6379> scan 17  # count 会使用上一次的 15
 1) "0"    # 游标值为 0,意味着遍历结束
 2) 1) "key:15"
    2) "key:118"
    3) "key:10"
    4) "key:112"
    5) "key:119"
    6) "key:13"
    7) "key:16"
    8) "key:19"    
    9) "key:111"

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注