当前位置:首页 > 问答 > 正文

Redis里怎么快点找出想要的keys,别用慢方法了

想在Redis里快点找出想要的keys,核心就一句话:尽量避免在生产环境直接用 KEYS 命令,这是所有Redis老手和新手都必须记住的第一条军令,下面我详细说说为什么,以及用什么方法来替代。

第一部分:为什么 KEYS 命令是“慢方法”的典型?

(来源:Redis官方文档在 KEYS 命令说明中的明确警告)

KEYS 命令的问题在于它的工作方式是“阻塞式”和“全表扫描”,你可以把Redis的数据库想象成一个巨大的、没有目录的字典,当你在客户端输入 KEYS user:123* 这个命令时,Redis服务器会做一件很可怕的事情:它会暂停所有其他操作,然后一页一页地、从头到尾翻遍整个字典,把所有符合 user:123* 这个模式的键找出来,再一次性返回给你。

这个过程有两个致命伤:

  1. 阻塞: 在Redis翻字典的这段时间里(可能几毫秒,也可能几秒甚至更长,取决于你的数据量),整个Redis服务器是无法响应其他任何读写命令的,这会导致你的应用程序卡住,超时,对于高并发的线上服务来说,这简直是灾难,官方文档直接说它“可能使服务器挂起数秒”。
  2. 性能随数据量线性下降: 你的数据库里有100个key,KEYS 命令可能瞬间完成,但有100万个key呢?1000万个key呢?它的速度会越来越慢,因为每次都是检查所有key。

KEYS 命令只应该用在测试环境、数据量极小或者你明确知道可以接受服务短暂不可用的情况下。 线上环境绝对不要用。

第二部分:推荐的快方法——使用 SCAN 命令

(来源:Redis官方文档为解决 KEYS 命令问题而引入的 SCAN 系列命令)

既然KEYS是“一口气翻完字典”,那SCAN命令就是“分批分次地翻字典”,它解决了KEYS命令的两个核心痛点。

SCAN 命令的基本用法是 SCAN cursor [MATCH pattern] [COUNT count]

  • cursor 是一个游标,第一次遍历时设为0,表示开始一次新的迭代。
  • MATCH patternKEYS 命令后面的模式一样,用于匹配键名。
  • COUNT count 是建议每次迭代返回多少元素,注意这只是个“建议值”,Redis不一定完全遵守,但大体上每次返回的数量会在这个值附近。

它的工作流程是这样的:

  1. 你第一次调用 SCAN 0 MATCH user:* COUNT 100,Redis会从数据库里扫描大概100个key,然后把满足 user:* 模式的key返回给你,同时返回一个新的游标值,65
  2. 你接着调用 SCAN 65 MATCH user:* COUNT 100,Redis会从上次停止的地方(游标65的位置)继续扫描大概100个key,再返回结果和一个新的游标。
  3. 如此反复,直到Redis返回的游标是 0,这表示整个数据库已经遍历完毕,所有匹配的key你都拿到了。

SCAN 快在哪里?

  • 非阻塞: 每次调用 SCAN 都只花费很短的时间,不会长时间阻塞服务器,其他客户端的请求可以在两次 SCAN 调用的间隙得到处理,虽然整个遍历过程的总耗时可能比一次性的 KEYS 命令还要长,但它把时间碎片化了,不影响整体服务的可用性。
  • 可控的消耗: 你可以通过 COUNT 参数来控制每次扫描的强度,根据服务器的繁忙程度灵活调整。

第三部分:使用 SCAN 需要注意的细节

(来源:Redis官方文档对 SCAN 命令特性的说明)

  1. 重复与遗漏: SCAN 命令在遍历过程中,如果数据库有增删改,可能会出现某个key被返回多次或者一次都没返回的情况,这是因为它是基于游标的增量迭代,无法提供完整的快照保证,如果你的应用场景要求遍历过程中数据绝对一致(比如精确统计),SCAN 可能不太适合,但对于大多数“找出key并处理”的场景,这个缺点是可以接受的。
  2. 不止一种 SCAN 除了遍历所有key的 SCAN,Redis还提供了针对特定数据结构的遍历命令:
    • SSCAN 用于遍历集合(Set)中的元素。
    • HSCAN 用于遍历哈希(Hash)中的所有字段和值。
    • ZSCAN 用于遍历有序集合(Sorted Set)中的成员和分值。 当你需要处理大集合、大哈希时,也应该使用这些命令来替代 SMEMBERSHGETALL 等可能阻塞服务器的命令。

第四部分:从根本上提速——好的设计优于任何命令

(来源:基于Redis最佳实践的普遍认知)

最高级的“快方法”不是等数据存进去了再想办法找,而是在设计阶段就避免大规模的模式匹配查询,Redis本质上是键值存储,最擅长的就是通过明确的key直接获取value,频繁使用 SCANKEYS 往往意味着数据模型设计可以优化。

  1. 使用索引集合: 这是最有效的方法,你想快速找到所有 user:123: 开头的key(user:123:profile, user:123:orders),你可以在创建这些key的同时,也往一个叫 index:user:123 的集合(Set)里添加这些key的名称(或它们的部分标识),这样,当你需要找所有属于用户123的key时,直接 SMEMBERS index:user:123 就行了,速度极快,复杂度是O(1),这相当于你自己手动维护了一个“目录”。
  2. 合理设计键名: 如果不同的数据有关联,可以把关联信息设计在键名里,把用户ID和订单ID组合成 order:{userId}:{orderId},这样虽然你还是需要 SCAN,但你的匹配模式可以更精确,扫描范围更小。
  3. 考虑其他工具: 如果你需要非常复杂的查询,比如范围查询、多条件组合查询,那可能Redis本身就不是最合适的工具,可以考虑使用RedisJSON模块,或者将数据同步到专门的搜索数据库(如Elasticsearch)中进行处理。

  • 绝对禁止在线上环境使用 KEYS 命令。
  • 需要遍历或模式匹配时,首选 SCAN 系列命令,它通过非阻塞、分批的方式解决了性能问题。
  • 最高境界是通过良好的数据模型设计(如使用索引集合),从根本上避免大规模的模式匹配,让查询永远是通过直接键名访问,这才是最快的方案。

Redis里怎么快点找出想要的keys,别用慢方法了