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

Redis之Keys命令和Scan命令

序言

网上看到的面试题:Redis有1亿个key,其中10w个key是以某个固定的前缀开头,如何将它们全部找出来?一般有两种命令可以实现:

  • Keys命令
  • Scan命令

下面具体分析一下两种命令

Keys命令

Keys pattern

如下图所示,建了一些不同数据结构(String、Hash、List、Set、ZSet)的Key,使用命令找出前缀为prefix的Key(key区分大小写),
在这里插入图片描述

  • 时间复杂度O(n);
  • 笔记本电脑40毫秒内可以查100w个键值对;
  • 生产环境慎用,大型数据库上执行会影响性能;

那么在大型数据库场景下为什么不能使用该命令呢?

Keys命令是一个阻塞式操作。

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

Scan命令

SCAN cursor [MATCH pattern] [COUNT count] [TYPE type]

在这里插入图片描述

  • 单次调用时间复杂度O(1),完整迭代时间复杂度O(n);所以单次调用不会长时间阻塞线上服务;
  • 非阻塞式(渐进式)迭代:该命令允许增量迭代,每次调用返回少量数据,然后返回一个游标(cursor),下次传入这个游标,Redis就会接着上次结束的地方继续扫描,如上图scan xxx match prefix* count 3每次返回的游标不一定按照顺序;
  • COUNT参数:COUNT是一个建议值,告诉Redis希望每次迭代返回大约多少个key。但不是精确的,有时多有时少,但是可以控制单次扫描的粒度。如上图scan xxx match prefix* count 3每次返回的数据量不一定都是3;

参看官网,相关的还有其他的一些扫描指令:

  • SSCAN:iterates elements of Sets types.(针对Set数据结构)
  • HSCAN:iterates fields of Hash types and their associated values.(针对Hash数据结构)
  • ZSCAN:iterates elements of Sorted Set types and their associated scores.(针对ZSet数据结构)

在这里插入图片描述

实际操作

实际项目中可能会通过python脚本或者LUA脚本去执行查找,下面是使用python代码去执行的示例代码,

import redisr = redis.Redis(host='localhost', port=6379)
cursor = 0
prefix = 'your_prefix:*'
found_keys = []while True:cursor, keys = r.scan(cursor, match=prefix, count=10000)found_keys.extend(keys)if cursor == 0:break

可以使用Lua脚本在Redis服务器端直接处理,减少网络往返:

local keys = redis.call('SCAN', ARGV[1], 'MATCH', ARGV[2], 'COUNT', ARGV[3])
return keys

其他思路

空间换时间

在这里插入图片描述

另外维护一个“索引”:

  • 创建Key时,将key添加到前缀索引的Set集合中;
  • 查找Key时,直接读取索引Set的所有成员,这样时间复杂度即使是O(n),遍历的也是10w数据,而非NoSQL数据库中的1亿条数据;
  • 删除时,除了删除原始key,还需要从索引Set中移除对应的成员;

从节点(Replica)执行

假设这是一个低频且用于离线分析的需求,并且不想修改现有的数据结构,可以考虑在Redis集群(主从、哨兵、集群模式)的从节点去执行SCAN命令操作(甚至Keys命令),避免长时间耗时影响主节点(Master)的正常读写。
在这里插入图片描述

http://www.xdnf.cn/news/18796.html

相关文章:

  • 在python 代码中调用rust 源码库操作步骤
  • mysql优化-mysql索引下推
  • LeetCode - 946. 验证栈序列
  • Linux-孤儿进程和僵死进程
  • mysql是怎样运行的(梳理)
  • Python包管理与安装机制详解
  • EasyExcel 3.x 导出动态表头,动态sheet页
  • Rust:函数与控制流
  • 《Java反射与动态代理详解:从原理到实践》
  • 【Ansible】Ansible部署K8s集群--准备环境--配置网络
  • PEFT 模型解析(59)
  • 《数据之心》——鱼小妖的觉醒
  • ctfshow_萌新web16-web20-----文件包含日志注入
  • 《信息检索与论文写作》实验报告二 引文索引数据库检索
  • 我们来学mysql -- safe启动
  • 解析xml文件并录入数据库
  • 类似ant design和element ui的八大Vue的UI框架详解优雅草卓伊凡
  • Vue中的scoped属性
  • 推荐系统王树森(三)粗排精排
  • 【NER学习笔记】:基于AdaSeq的NER模型训练笔记
  • Linux下TCPT通信
  • 8.26 支持向量机
  • 什么样的 IP 能穿越周期,持续被用户买单?​
  • 基于大模型的智能占卜系统实战-Qwen-VL、RAG、FastAPI
  • “喵汪联盟”宠物领养系统的设计与实现(代码+数据库+LW)
  • Python编程快速上手—让繁琐工作自动化
  • OpenCV打开视频函数VideoCapture使用详解
  • 数据与端点安全 (Protect data and apps)
  • 【学习笔记】系统时间跳变会影响time接口解决措施
  • Matlab使用——开发上位机APP,通过串口显示来自单片机的电压电流曲线,实现光伏I-V特性监测的设计