在Redis中查找匹配Key需使用SCAN命令而非KEYS,遍历所有Key同样依赖SCAN迭代,这是避免阻塞生产环境的唯一标准做法。
很多刚接触Redis的开发者都会遇到一个经典误区:试图用类似Access数据库那样简单的查询语句去查找数据,Redis作为内存数据库,其设计哲学与关系型数据库截然不同,如果你在生产环境中直接使用KEYS 或KEYS pattern来查找Key,后果往往是灾难性的,业内专家指出,单线程模型决定了Redis在处理命令时是串行的,任何阻塞操作都会导致整个服务不可用,掌握非阻塞的遍历机制,是每一位后端工程师的必修课。
为什么严禁在生产环境使用KEYS命令
要理解如何正确遍历,首先得明白为什么不能“偷懒”。KEYS命令虽然语法简单,支持通配符如KEYS user:,但它存在两个致命缺陷。
性能阻塞风险
Redis是单线程处理网络请求的,当执行KEYS命令时,Redis需要扫描数据库中的所有Key,如果数据库中存储了数百万甚至上千万个Key,这个扫描过程会持续数秒甚至更久,在这期间,Redis无法处理任何其他的读写请求,导致客户端超时、连接断开,甚至引发雪崩效应,据统计,在大型互联网应用中,因误用KEYS命令导致的线上故障占比相当一部分。
内存消耗巨大
KEYS命令会将所有匹配到的Key一次性返回给客户端,如果匹配结果成千上万,这将导致网络带宽激增,客户端内存瞬间飙升,可能直接导致应用服务OOM(内存溢出)崩溃,这种操作在资源受限的云环境中尤为危险。
使用SCAN命令安全遍历所有Key
既然KEYS不可用,那么如何安全地遍历所有Key或查找匹配Key呢?答案只有一个:SCAN命令。SCAN命令采用游标(Cursor)迭代的方式,每次只返回少量数据,从而将对Redis主线程的影响降到最低。

SCAN命令的基本语法
SCAN命令的核心在于“游标”,你不需要一次性获取所有数据,而是通过不断获取新的游标,直到游标返回0,表示遍历结束。
- 初始调用:使用游标
0开始遍历。 - 迭代调用:使用上一次返回的游标继续遍历。
- 结束条件:当返回的游标为
0时,遍历完成。
实操步骤与代码示例
以Python的redis-py客户端为例,这是目前最流行的Redis客户端之一,以下是遍历所有Key的标准写法:
import redis
r = redis.Redis(host='localhost', port=6379, db=0)
# 使用scan_iter,它内部封装了游标迭代逻辑
for key in r.scan_iter(match='user:', count=100):
print(key)
这里有两个关键参数需要注意:
match参数
用于指定匹配模式,例如user:或cache_,如果不指定,则遍历所有Key。
count参数
用于指定每次迭代返回的最大Key数量,注意,count只是一个建议值,Redis并不保证每次正好返回这么多Key,设置合理的count值(如100-1000)可以在性能和内存之间取得平衡。
如何高效查找匹配的Key
在实际业务场景中,我们往往不需要遍历所有Key,而是需要查找特定模式的Key,例如查找所有以session:开头的会话Key。
SCAN与KEYS的对比分析
| 特性 | KEYS命令 | SCAN命令 |
|---|---|---|
| 阻塞性 | 强阻塞,全量扫描 |
弱阻塞,增量迭代 |
| 适用场景 | 开发测试环境 | 生产环境 |
| 时间复杂度 | O(N),N为Key总数 | O(N),但分摊到多次调用 |
| 返回结果 | 一次性返回所有匹配Key | 分批返回匹配Key |
| 重复性 | 无重复 | 可能重复返回相同Key |
处理重复Key的策略
由于SCAN命令在迭代过程中,Redis可能会执行写操作(新增Key),这会导致某些Key被重复返回,在业务逻辑中,必须对Key进行去重处理。
- 使用Set存储结果:将遍历到的Key存入一个Set数据结构中,利用Set的唯一性自动去重。
- 业务层去重:在应用层维护一个已处理Key的列表,跳过已处理的Key。
场景化示例:清理过期Session
假设你需要清理所有过期的用户Session,以下是标准操作流程:
- 初始化游标为
0。 - 调用
SCAN cursor MATCH session: COUNT 100。 - 遍历返回的Key列表。
- 对每个Key检查过期时间或业务状态。
- 若需删除,调用
DEL key。 - 更新游标,若游标不为
0,重复步骤2-5。 - 当游标为
0时,结束遍历。
高级技巧:使用SSCAN和HSCAN
除了遍历Key,有时我们需要遍历Key内部的数据结构,如Set、Hash、ZSet等,这时需要使用SSCAN、HSCAN和ZSCAN

命令。
SSCAN:遍历Set成员
SSCAN用于遍历Set集合中的成员,查找某个用户的所有标签:
SSCAN myset 0 MATCH COUNT 100
HSCAN:遍历Hash字段
HSCAN用于遍历Hash中的字段和值,查找某个用户信息Hash中所有以name开头的字段:
HSCAN user:1001 0 MATCH name: COUNT 100
这些命令同样采用游标迭代,避免了全量加载导致的大Key问题。
常见问题解答
Redis中如何查找匹配的Key和遍历所有Key?
必须使用SCAN命令配合match参数进行模糊匹配,或使用不带match参数的SCAN遍历所有Key,严禁使用KEYS命令,因为它会阻塞Redis主线程,导致服务不可用。SCAN通过游标迭代,每次返回少量数据,确保系统稳定性。
SCAN命令返回的Key是否会有重复?
是的,SCAN命令在迭代过程中,如果Redis执行了写操作,可能会导致某些Key被重复返回,这是因为SCAN基于游标的实现机制,无法保证绝对的一致性,业务逻辑中必须对Key进行去重处理,例如使用Set集合存储已处理的Key。
SCAN命令的count参数越大越好吗?
并非如此。count参数仅作为建议值,Redis会根据内部实现调整实际返回数量,过大的count值可能导致单次命令返回数据过多,增加网络传输压力和客户端内存负担;过小的count值则会增加迭代次数,延长遍历总时间,建议根据实际数据量和网络带宽,将count设置在100到1000之间,并通过监控调整至最优值。
掌握SCAN命令的正确用法,不仅能避免生产事故,还能提升系统的可维护性和稳定性,在Redis的世界里,慢即是快,安全优于便捷。
首发原创文章,作者:世雄 - 原生数据库架构专家,如若转载,请注明出处:https://idctop.com/article/389401.html

