Redis KEY*模糊查询导致速度慢、阻塞其他 Redis 操作

2023-12-13 06:19:10

Redis KEY*模糊查询导致交互速度慢、阻塞其他 Redis 操作

在这里插入图片描述

查询速度慢的原因

在Redis中,使用通配符 KEYS 命令进行键的模糊匹配(比如 KEYS key*)可能会导致性能问题,尤其是在数据集较大时。这是因为 KEYS 命令的实现需要遍历所有的键来匹配模式,这个过程的时间复杂度是 O(N),其中 N 是键的总数,因此使用KEYS*命令查询时,Redis的响应速度和Redis中数据量成正比

在大规模的生产环境中,遍历所有键可能会导致阻塞其他 Redis 操作,因为 KEYS 命令会持有数据库的写锁。此外,这也可能对性能产生负面影响,因为它需要消耗大量的计算资源。在 Redis 中,所有命令都是按顺序执行的,一个命令执行完成后,才会执行下一个命令。这个单线程模型是 Redis 的设计选择之一,有助于简化并发控制,提高数据一致性。

使用带前缀的通配符 KEYS prefix*

在 Redis 中,使用带前缀的通配符 KEYS prefix* 仍然需要遍历所有匹配前缀的键。虽然带有前缀的通配符能够减小匹配的范围,但是在 Redis 中并没有内置的索引结构能够直接加速带有通配符的键的查找。

这是因为Redis 的键是以哈希表的形式存储的,通过哈希函数计算出键的位置。当使用通配符 KEYS prefix* 时,Redis 无法直接利用哈希函数来快速定位符合条件的键,而需要遍历哈希表中的所有键,然后筛选匹配的键。

如果对于特定的使用场景,你经常需要根据键的前缀来查询数据,可以考虑使用 Redis 的有序集合(Sorted Set)来模拟带有前缀的键的查询。在有序集合中,你可以将键的前缀作为分数,然后使用范围查询来获取符合条件的键。这样可以更有效地执行带有前缀的查询,但这需要根据具体情况来设计和维护有序集合。

总体来说,避免在生产环境中使用 KEYS 命令,尤其是在大规模数据集上,因为它可能导致性能问题。在生产环境中,当出现使用KEYS*的场景时,一定要谨慎,考虑如何改变reid key的数据结构来避免使用;如果非要遍历所有keys不可,更推荐使用迭代命令(如 SCAN)以及结合合适的数据结构来执行查询,它可以用于迭代集合中的元素而不会阻塞整个数据库,特别是在大型数据库中,因为它将迭代的工作分散到多个步骤,并允许客户端逐步处理结果。这样可以避免 KEYS 命令可能引起的性能问题。

特殊场景SCAN 一个用于迭代集合元素的命令

SCAN 是 Redis 提供的一个用于迭代集合元素的命令。它可以用于逐步迭代集合中的元素,而不会一次性获取所有元素。这样可以减小对 Redis 服务器的负载,并且不会阻塞其他的 Redis 命令。

SCAN 命令的一般用法是:

SCAN cursor [MATCH pattern] [COUNT count]

其中:

  • cursor 是一个用于标识迭代状态的整数,初始时通常设置为 0。
  • MATCH pattern 是一个可选参数,用于匹配指定的模式。
  • COUNT count 是一个可选参数,用于指定每次迭代返回的元素数量。

SCAN 命令会返回一个包含两个元素的数组,第一个元素是下一次迭代所需的新的 cursor,第二个元素是包含元素的数组。

例子:

SCAN 0 COUNT 10

这个命令将从集合的开头开始迭代,每次返回最多 10 个元素。

使用 SCAN 命令相比于 KEYS 命令,主要优势在于它不会阻塞 Redis 服务器太长时间,因为它可以分阶段地获取数据。KEYS 命令可能会导致阻塞,因为它要在一次性操作中返回所有匹配的键。

需要注意的是,由于 SCAN 是迭代器,返回的结果可能不是实时的快照,而是在迭代开始时的一个快照。因此,在迭代过程中,集合的内容可能会发生变化。


使用 SCAN 命令时,通常需要在程序中进行多次调用直到迭代结束。每次调用 SCAN 命令都会返回下一批符合条件的元素和新的游标值,程序需要根据新的游标值决定是否继续迭代。

基本的流程如下:

  1. 使用 SCAN 0 命令开始迭代,初始游标设置为 0。
  2. 处理 SCAN 命令返回的结果,包括下一批元素和新的游标值。
  3. 如果新的游标值为 0,表示迭代结束;否则,使用新的游标值再次调用 SCAN 命令,重复步骤 2。
  4. 继续处理迭代结果,直到迭代结束。

这样的迭代方式可以确保不会对 Redis 服务器造成较大的负载,同时适应大型数据集的处理。

示例(伪代码):

cursor = 0

while True:
    result = redis_client.scan(cursor, count=10)
    elements = result[1]
    # 处理当前批次的元素

    cursor = result[0]
    
    if cursor == 0:
        break

这样的迭代方式可以有效地处理大型数据集,减小对 Redis 服务器的压力。

文章来源:https://blog.csdn.net/a2272062968/article/details/134882018
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。