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

Redis怎么一次性找一堆key,批量查找多个key的那些事儿

(主要信息来自Redis官方文档关于KEYS和SCAN命令的说明,以及多个技术社区如Stack Overflow上关于批量操作的最佳实践讨论)

好的,直接说事儿,你想在Redis那个巨大的键值对仓库里,一次性找出好几个特定的key,而不是一个个去问,比如你有一堆用户数据,key的名字像是“user:1001”、“user:1002”、“user:1005”等等,你现在就想知道1001、1005和1008这三个人的数据在不在库里,这事儿在Redis里有几种不同的干法,每种都有它的脾气,用对了省心省力,用错了可能给自己挖坑。

第一种方法,也是最直接但最不推荐的方法:用KEYS命令配合通配符。

你可能会想,既然能像找文件那样用号来模糊查找,那我是不是可以写成KEYS user:1001 user:1005 user:1008?不行,Redis的KEYS命令没那么聪明,它一次只能接受一个模式(pattern),如果你想用KEYS来找,你得想一个能同时匹配这三个key的通配符模式,但这通常很困难,因为你的key可能毫无规律,除非你这几个key恰好有共同的前缀,比如都是“user:100”开头,那你用KEYS user:100*就能把1001、1002这些都捞出来,但这样会找出所有以“user:100”开头的key,远远超出了你只需要那三个的本意,会带回大量你不想要的数据。

更重要的是,KEYS命令有个非常要命的缺点:它会阻塞Redis服务器,Redis是单线程的,它同一时间只能干一件事,当你对一个存储了百万甚至千万级key的Redis实例使用KEYS命令时,这个命令会遍历整个数据库的所有key,就像让一个人一口气翻遍一个巨型仓库的每个角落,在这个过程中,Redis就无法处理其他的任何命令(比如别的客户端的读取或写入请求),会导致服务暂时卡死,在任何生产环境(也就是正式上线的系统)中,都绝对禁止使用KEYS命令,这个警告在Redis官方文档里是明确指出的。

第二种方法,也是正确的方法:用SCAN命令迭代遍历。

Redis怎么一次性找一堆key,批量查找多个key的那些事儿

既然KEYS命令这么危险,Redis提供了一个安全的替代品:SCAN命令,SCAN的理念是“慢慢来,分批次找”,它不会一次性扫描所有key,而是每次只扫描一小部分,然后返回这部分里匹配的key和一个游标(cursor),你拿到这个游标后,下次带着这个游标再调用SCAN,它就从上次停下的地方继续扫描,这样就把一个可能耗时的巨大任务,拆成了很多个快速的小任务,中间就可以插队处理其他请求,服务器就不会被阻塞。

SCAN命令和KEYS一样,也是基于模式匹配的,如果你无法用一个通配符模式精确描述出“user:1001, user:1005, user:1008”这个集合,那么SCAN用起来也会很麻烦,你可能需要扫描整个数据库,然后在自己程序里过滤出你要的那几个key,效率依然不高。

第三种方法,也是最针对你这种需求的方法:用MGET或Pipeline(管道)批量操作。

Redis怎么一次性找一堆key,批量查找多个key的那些事儿

当你知道确切的key名字,只是想看看它们对应的值是否存在或者是什么的时候,最佳选择不是“查找”命令,而是“读取”命令。

  1. MGET命令:这是最简单的方式,你可以直接一条命令发过去:MGET user:1001 user:1005 user:1008,Redis会一次性返回这三个key对应的值,如果某个key不存在,返回结果里对应位置就是nil,这个方法非常高效,因为一次网络往返就解决了所有问题,但MGET的局限是它只能用于获取值(value)。

  2. Pipeline(管道):如果你的需求不只是获取值,比如你还想检查key的类型、过期时间等等,或者你需要进行的操作MGET不支持,那么Pipeline就是你的王牌,它的思路是:你不是要发多个命令吗(比如连续发三个EXISTS user:1001EXISTS user:1005EXISTS user:1008)?正常情况下,发一个命令,等一个回复,再发下一个,网络来回次数太多,Pipeline让你可以把这三个命令打包成一个数据包,一次性发给Redis服务器,Redis服务器会按顺序执行所有这些命令,然后把所有结果再打包成一个数据包一次性返回给你,这极大地减少了网络通信的开销,速度比一个个发命令快得多。

  • 千万别用KEYS命令,除非是在本地测试环境玩一下。
  • 如果你要找的key有共同的模式,并且可以接受慢慢遍历,用SCAN
  • 如果你已经明确知道要找的是哪几个具体的key,并且只是想获取它们的值,直接用MGET,最省事。
  • 如果你已经明确知道是哪几个具体的key,但需要对这些key执行MGET以外的多种操作,那就用Pipeline,把多个命令打包发送。

回到你“一次性找一堆key”的问题上,核心其实不在于“查找”,而在于“批量操作”,只要你手里有那份准确的key名单,MGET和Pipeline就是为你量身定做的工具,而SCAN则是在你只知道模糊特征、需要从海量数据中筛选时,那个虽然慢但不会闯祸的可靠帮手。