当前位置: 首页 > news >正文

京东一面:假如Redis里面有1亿个key,其中有10w个key是以某个固定的已知的前缀开头的,如何将它们全部找出来?

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

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

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

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

KEYS "your_prefix:*"

为什么这是错误的答案?

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

  1. 单线程模型: Redis 的主要命令处理是单线程的。一个命令在执行时,其他所有客户端的请求都必须等待。
  2. 全量遍历: KEYS 为了找出所有匹配的 key,会遍历数据库中的全部 1 亿个 key。在遍历完成之前,Redis 无法处理任何其他命令。
  3. 生产环境灾难: 在一个有 1 亿 key 的实例上执行 KEYS,会导致 Redis 服务卡顿数十秒甚至数分钟,所有依赖 Redis 的业务都会出现超时和雪崩,这是严重的生产事故。

结论: 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 是标准答案?

  1. 非阻塞式(渐进式)迭代: SCAN 命令不是一次性返回所有结果。它每次只扫描一小部分数据,然后返回一个游标(cursor)。你下次调用时传入这个游标,Redis 就会从上次结束的地方继续扫描。这个过程是无锁的。
  2. 对业务无影响: 由于每次扫描的计算量很小(可以把一次SCAN看作 O(1) 操作),它不会长时间阻塞 Redis 主线程,对线上服务的影响极小。
  3. COUNT 参数: COUNT 只是一个建议值,告诉 Redis 你希望每次迭代返回大约多少个 key。它不是精确的,有时多有时少,但可以用来控制单次扫描的粒度。

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

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

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

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

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

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

具体做法:

  1. 写入时:
    • 当你创建一个 key,例如 SET "your_prefix:123" "some_value"
    • 同时,将这个 key 的唯一部分(如 123)添加到一个固定的 Set 中。
    • SADD "index:your_prefix" "123"
  1. 查找时:
    • 不再需要 SCAN。直接读取索引 Set 的所有成员。
    • SMEMBERS "index:your_prefix"
    • 这个命令会立即返回所有 your_prefix 后缀的 key("123", "456", ...),时间复杂度是 O(N),其中 N 是 Set 中的成员数量(10w),而不是数据库的总 key 数(1亿)。这非常快。
  1. 删除时:
    • 删除原始 key 的同时,也要从索引 Set 中移除对应的成员。
    • DEL "your_prefix:123"
    • SREM "index:your_prefix" "123"

优缺点:

  • 优点: 查询性能极高,从根本上解决了扫描问题。
  • 缺点: 增加了写入/删除时的操作复杂度,并且会占用额外的内存来存储索引。
方案二:在从节点(Replica)上执行

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

  • SCAN(甚至是 KEYS,如果该从节点不服务其他业务)的操作放在 Redis 的从节点上执行。
  • 这样可以确保即使操作耗时较长,也完全不会影响到主节点(Master)的正常读写,从而隔离了风险。

面试回答总结

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

  1. 首先,绝对不能使用 KEYS "your_prefix:*" 命令。因为 KEYS 是一个阻塞操作,它会锁住 Redis 并遍历所有 1 亿个 key,导致线上服务出现严重卡顿,这是生产环境的禁忌。
  2. 推荐的标准做法是使用 SCAN 命令SCAN 是一个非阻塞的、渐进式的迭代命令。我们可以从游标 0 开始,配合 MATCH "your_prefix:*"COUNT 参数,通过循环迭代的方式分批次地将这 10w 个 key 找出来。这个过程不会阻塞主线程,对线上服务影响极小,是安全可靠的操作。
  3. 从架构设计的角度看,一个更治本的方案是在写入时就维护一个索引。例如,我们可以用一个固定的 Set 集合来存储所有这些特殊前缀 key 的唯一标识。当需要找出它们时,直接使用 SMEMBERS 命令读取这个 Set 即可,速度极快,避免了任何扫描操作。当然,这需要在业务代码中增加维护索引的逻辑。

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


文章转载自:

http://2JLkDUTn.wmhqd.cn
http://K1bYHFeq.wmhqd.cn
http://3UTuDTe5.wmhqd.cn
http://YxndReHw.wmhqd.cn
http://ovB8xTh0.wmhqd.cn
http://KdZ6tqaZ.wmhqd.cn
http://GNxt2DHE.wmhqd.cn
http://SdqbF6n8.wmhqd.cn
http://gwBQOctG.wmhqd.cn
http://OdPt7hcJ.wmhqd.cn
http://JYOW8BY0.wmhqd.cn
http://UhBvs2v9.wmhqd.cn
http://eYxKb4PN.wmhqd.cn
http://au5uyFiQ.wmhqd.cn
http://vjQ12ONa.wmhqd.cn
http://8v7SgErY.wmhqd.cn
http://lrm3bBCM.wmhqd.cn
http://saHjBeur.wmhqd.cn
http://5krLvnL7.wmhqd.cn
http://JReaBSDH.wmhqd.cn
http://6REyhLeE.wmhqd.cn
http://1aLZfHIn.wmhqd.cn
http://POEUmLD8.wmhqd.cn
http://00rjfGBz.wmhqd.cn
http://MyBfptiq.wmhqd.cn
http://MAAv4XfI.wmhqd.cn
http://7honw2iG.wmhqd.cn
http://Qhu6nrty.wmhqd.cn
http://LbIZmh7U.wmhqd.cn
http://WU4XVEgP.wmhqd.cn
http://www.dtcms.com/a/364796.html

相关文章:

  • Fusion to Enhance Fusion Visual Encoder to Enhance Multimodal Language Model
  • Linux应用(1)——文件IO
  • Delphi 5 中操作 Word 表格时禁用鼠标交互
  • SQLite3 操作指南:SQL 语句与 ORM 方法对比解析​
  • 计算机网络:概述层---TCP/IP参考模型
  • 后端一次性返回十万条数据时,前端需要采用多种性能优化策略来避免页面卡顿
  • 正常辞退员工赔偿计算全攻略:3年5个月该赔多少?
  • C++学习 part1
  • python中`__annotations__` 和 `inspect` 模块区别??
  • 【Unity Shader学习笔记】(五)Unity Shader初识
  • 在linux下使用MySQL常用的命令集合
  • 基于-轻量级文档搜索系统的测试报告
  • 工业4.0时代的通信革命:OPC UA Pub/Sub机制全面解析
  • 车载诊断架构 --- 从架构系统角度怎么确保整车DTC的完整性?
  • 关于缓存的一些思考?
  • SPI通讯协议
  • 【AI】人工智能 传统和现代 架构和算法的演变历史
  • 控制View缩放与还原
  • Go 语言面试题详解之上下文 (Context) 解密
  • 开学季 老师必备的收集信息“神器”
  • 利用 Python 绘制环形热力图
  • 使用飞算JavaAI快速搭建酒店管理系统
  • Augmentcode免费额度AI开发WordPress商城实战
  • VBA 64位API声明语句第013讲
  • leetcode算法刷题的第二十五天
  • LiteFlow:国产流程编排引擎体验
  • 疯狂星期四文案网第59天运营日记
  • 机器学习从入门到精通 - 降维艺术:PCA与t-SNE带你玩转高维数据可视化
  • 【Unity开发】热更新学习——AssetBundle
  • 【wpf】WPF 自定义控件绑定数据对象的最佳实践