dbaplus社群 11月06日 08:52
Redis大Key查找:从错误到优化的全面解析
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

在处理拥有上亿键的Redis实例时,如何安全高效地查找特定前缀的键是一个关键挑战。文章首先指出,直接使用KEYS命令是不可取的,因为它会阻塞Redis主线程,可能导致生产环境的严重故障。随后,文章详细介绍了使用SCAN命令进行渐进式迭代查找的正确方法,强调了其非阻塞特性,并提供了具体的命令示例和客户端实现思路。最后,文章进一步升华,提出了通过维护Set或Hash集合作为索引的架构设计思路,从根源上解决全量扫描问题,并讨论了在从节点执行扫描操作以隔离风险的方案,为面试和生产实践提供了多层次的解决方案。

❌ **避免使用 KEYS 命令进行全量扫描**:KEYS命令是一个阻塞式操作,会遍历Redis实例中的所有键,在拥有大量键(如1亿)的生产环境中执行,会导致Redis服务长时间卡顿,影响所有依赖Redis的业务,是严重的生产事故,仅适用于调试或键数量极少的场景。

✅ **标准安全做法:使用 SCAN 命令进行渐进式迭代**:SCAN命令是为解决KEYS命令的阻塞问题而设计的。它通过游标(cursor)进行非阻塞的、分批次的数据扫描。客户端需要循环调用SCAN,传入上一次返回的游标,直到游标值为0,这样可以最大程度地减少对线上服务的性能影响,是生产环境中查找大Key的标准方法。

🚀 **架构优化:利用集合 (Set/Hash) 作为索引**:从根本上解决全量扫描需求,可以在写入数据时就维护一个索引。例如,将特定前缀键的标识符存储在一个固定的Set集合中。查找时,直接使用SMEMBERS命令获取Set的成员,其性能远高于SCAN,但增加了写入/删除操作的复杂度和内存占用。

🛡️ **隔离风险:在从节点执行扫描**:如果查找需求是低频的、用于离线分析,且不希望修改现有数据结构,可以将SCAN(甚至KEYS,若从节点不服务其他业务)操作放在Redis的从节点上执行。这样可以完全隔离对主节点的性能影响,确保主服务的可用性。

Fox 2025-11-06 07:15 广东

考察你对Redis底层原理、性能影响以及生产环境实践的综合理解。

这是一个非常经典的 Redis 面试题,它考察的不仅仅是你知不知道某个命令,更是你对 Redis 底层原理、性能影响以及生产环境实践的综合理解。

我会分层次地回答这个问题,从“错误答案”到“标准答案”,再到“加分答案”。

层级一:错误或有严重风险的答案 (KEYS)

最直接、最容易想到的方法是使用 KEYS 命令:

KEYS "your_prefix:*"

为什么这是错误的答案?

在面试中,如果你只回答这个,基本就结束了。因为KEYS命令是一个阻塞式操作。

结论:KEYS 命令只能在调试或 key 总量极少的场景下使用,严禁在线上生产环境对大规模实例使用。

层级二:标准且安全的答案 (SCAN)

正确的操作应该使用 SCAN 命令,它是为解决 KEYS 的阻塞问题而设计的。

# 第一次执行,从游标 0 开始
SCAN 0 MATCH "your_prefix:*" COUNT 1000


# Redis 返回结果
# 1) "1762"  <-- 这是下一次迭代要用的新游标
# 2) 1) "your_prefix:key1"
#    2) "your_prefix:key2"
#    ... (返回一批 key)


# 第二次执行,使用上一次返回的新游标 "1762"
SCAN 1762 MATCH "your_prefix:*" COUNT 1000


# ... 重复这个过程,直到返回的游标为 "0" 时,表示遍历完成。

为什么SCAN是标准答案?

实现方式:

你需要在你的客户端代码中(例如 Java, Python, Go)编写一个循环,不断调用SCAN,直到返回的游标为 "0",并将每次返回的结果聚合起来。

层级三:更优的架构设计(加分答案)

面试官问这个问题,其实也想考察你是否具备良好的数据结构设计能力。一个优秀的架构师会思考:“我们是否可以从根源上避免这种全量扫描的需求?”

如果你能提出以下方案,会非常加分:

方案一:使用集合 (Set) 或哈希 (Hash) 作为索引

这是最优的解决方案。在写入数据时,除了存储原始的 key-value,我们还应该维护一个“索引”。

具体做法:

写入时:

查找时:

删除时:

优缺点:

方案二:在从节点(Replica)上执行

如果这是一个低频的、用于离线分析的需求,并且你不想修改现有的数据结构,可以考虑:

面试回答总结

面试官您好,对于这个问题,我的回答如下:

首先,绝对不能使用KEYS "your_prefix:*"命令。因为KEYS是一个阻塞操作,它会锁住 Redis 并遍历所有 1 亿个 key,导致线上服务出现严重卡顿,这是生产环境的禁忌。

推荐的标准做法是使用SCAN命令。SCAN是一个非阻塞的、渐进式的迭代命令。我们可以从游标 0 开始,配合MATCH "your_prefix:*"和COUNT参数,通过循环迭代的方式分批次地将这 10w 个 key 找出来。这个过程不会阻塞主线程,对线上服务影响极小,是安全可靠的操作。

从架构设计的角度看,一个更治本的方案是在写入时就维护一个索引。例如,我们可以用一个固定的Set集合来存储所有这些特殊前缀 key 的唯一标识。当需要找出它们时,直接使用SMEMBERS命令读取这个 Set 即可,速度极快,避免了任何扫描操作。当然,这需要在业务代码中增加维护索引的逻辑。

所以,对于这个临时性的查找需求,我会选择 SCAN;如果这是一个频繁的操作,我会建议通过维护索引的方式来优化架构。

作者丨Fox

来源丨公众号:Fox爱分享(ID:dcl_yc)

dbaplus社群欢迎广大技术人员投稿,投稿邮箱:editor@dbaplus.cn

阅读原文

跳转微信打开

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

Redis 大Key SCAN KEYS 性能优化 架构设计 面试题 生产实践 Set Hash 从节点
相关文章