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

Redis里怎么把所有元素都翻一遍,方法和技巧分享

要理解Redis里“把所有元素都翻一遍”这个需求,通常指的是遍历整个数据库或者某个特定数据类型中的所有键(Key)或键值对,Redis本身是键值存储,所以核心是遍历键,但根据你的目的不同,比如是想看看有哪些键、想获取所有键的值、或者想对某些键进行操作,方法和注意事项也完全不同,最重要的一点是,*在Redis中,绝对不要在生产环境中使用`KEYS `这个命令**,下面我会详细解释为什么,以及正确的做法。

*第一部分:为什么严禁使用 KEYS 命令?**

这个命令是很多人第一个想到的,因为它很简单,输入KEYS *,Redis就会把数据库里所有的键都列出来,但这是一个非常危险的操作,被戏称为“毁灭性命令”,原因在于Redis是单线程的,这意味着Redis在同一时间只能处理一个命令。

当你执行KEYS *时,如果数据库里有几百万甚至上千万个键,Redis必须遍历整个键空间,这是一个O(N)复杂度的操作(N是数据库里的键总数),在这个命令执行期间,Redis服务器会被这个命令完全“霸占”,无法处理任何其他的读写请求,会导致服务暂时卡死、超时,对于线上业务来说,这几乎是灾难性的,任何情况下,都不要在正式运营的服务器上使用KEYS *

第二部分:安全遍历的“瑞士军刀”——SCAN 命令

Redis里怎么把所有元素都翻一遍,方法和技巧分享

既然KEYS *不能用,那该怎么办?Redis从2.8版本开始,提供了一个安全、可迭代的遍历命令:SCANSCAN命令是解决这个问题的标准答案。

  1. 工作原理SCAN命令不是一次性返回所有结果,而是采用了一种游标分批次遍历的方式,你第一次执行SCAN 0(0代表开始),它会返回两部分内容:一个是下一次遍历需要的游标值(比如123),另一个是本次扫描到的少量键的列表(比如10个),然后你再用SCAN 123去获取下一批,如此反复,直到返回的游标值再次变成0,就意味着整个数据库遍历完毕。

  2. 优点

    • 不阻塞:因为每次只扫描一小部分,所以每次执行速度都很快,不会长时间阻塞服务器,其他命令可以正常执行。
    • 可中断:即使在遍历过程中你停止了操作,也不会有什么负面影响,因为服务器没有被阻塞。
    • 一致性:虽然它不保证在遍历过程中如果键发生变化(增删改)能完全反映所有变化,但它会保证至少返回遍历开始那一刻存在的所有键,不会漏掉(但可能会有少量重复,需要客户端去重)。

第三部分:SCAN 命令的具体用法和技巧

Redis里怎么把所有元素都翻一遍,方法和技巧分享

SCAN命令有几个变种,用于遍历不同类型的元素:

  • SCAN:遍历数据库中的所有键。
  • SSCAN key:遍历指定集合(Set)中的所有成员。
  • HSCAN key:遍历指定哈希(Hash)中的所有字段和值。
  • ZSCAN key:遍历指定有序集合(Sorted Set)中的成员和分值。

这里以最常用的SCAN为例,讲一下使用技巧:

  • 基本语法SCAN cursor [MATCH pattern] [COUNT count]

    • cursor:游标,第一次传0,之后传上一次命令返回的游标。
    • MATCH pattern:可选参数,用于匹配键的模式,类似于KEYS命令后的模式,比如SCAN 0 MATCH user:*只找以user:开头的键。
    • COUNT count:可选参数,建议每次遍历的数量,注意,这只是一个“提示”,Redis不一定完全遵守,可能会返回比COUNT多或少的元素,默认值是10,你可以根据数据库大小调整,比如设为1000,但不要设得太大,否则又会失去非阻塞的优势。
  • 使用示例(在命令行中)

    Redis里怎么把所有元素都翻一遍,方法和技巧分享

    0.0.1:6379> SCAN 0 COUNT 5
    1) "12"                    # 下一次的游标是12
    2) 1) "key:redis:1"        # 本次返回的5个键
       2) "key:2"
       3) "key:3"
       4) "key:4"
       5) "key:5"
    127.0.0.1:6379> SCAN 12 COUNT 5
    1) "0"                     # 游标为0,表示遍历结束
    2) 1) "key:6"              # 最后一批键
       2) "key:7"
  • 编程中的使用:在Python、Java等语言中,Redis客户端通常都对SCAN命令有很好的封装,会提供一个迭代器,你只需要循环遍历即可,不用手动管理游标,例如在Python的redis-py库中:

    import redis
    r = redis.Redis(host='localhost', port=6379, db=0)
    # 安全地遍历所有键
    for key in r.scan_iter():
        print(key)
    # 只遍历以"session:"开头的键,并每次处理100个
    for key in r.scan_iter(match='session:*', count=100):
        value = r.get(key)  # 获取键的值
        # ... 对你的键值对进行操作 ...

第四部分:其他场景和技巧

  1. 如果数据库很小,确定没风险,就想快速看一眼:有时候在测试环境或者确认数据库中只有几十个键的情况下,用KEYS *图个方便也不是不行,但一定要养成习惯,先通过DBSIZE命令看一下键的总数,如果数量很大,就坚决不用。

  2. 只想随机取几个键看看:可以使用RANDOMKEY命令,它会随机返回数据库中的一个键,这个命令很快,不会阻塞。

  3. 遍历大Value的技巧:你不仅需要遍历键,还需要获取值,如果某个键对应的Value非常大(比如一个很大的Hash或List),直接GET它可能会占用大量网络带宽和内存,这时候,如果只是检查内容,可以考虑使用HSCANSSCAN等命令来分块获取大Value的内部元素,而不是一次性全部取出。

总结一下核心思想

“翻一遍”Redis的数据,核心工具是SCAN系列命令,关键在于理解分批次非阻塞的重要性,抛弃KEYS *这个危险的念头,熟练掌握SCAN及其在不同数据类型(Set、Hash、Sorted Set)上的变种用法,就能在各种场景下安全、高效地完成遍历任务,对线上环境的操作,安全永远是第一位的。 来源:基于Redis官方文档中对KEYS和SCAN命令的说明,以及普遍认可的Redis运维最佳实践。)