Redis中渐进式命令scan详解与使用

 更新时间:2025年11月13日 09:59:40   作者:兜兜风d  
在Redis日常开发中,我们经常需要遍历数据库中的键或集合中的元素,scan命令是Redis 2.8版本引入的渐进式遍历方案,解决了keys命令在大数据量下阻塞Redis服务的问题,下面就来介绍一下该命令的使用,感兴趣的可以了解一下

在 Redis 日常开发中,我们经常需要遍历数据库中的键或集合中的元素。传统的keys命令虽然简单直接,但在数据量较大时会严重阻塞 Redis 服务,甚至引发生产事故。而scan命令作为 Redis 2.8 版本引入的渐进式遍历方案,完美解决了这一痛点。

一、为什么需要 scan?先看 keys 命令的 “坑”

在了解scan之前,我们首先要明确:为什么不能在生产环境中随意使用keys命令?

keys命令的工作方式是全量遍历—— 它会一次性遍历 Redis 中的所有键,并将符合模式的结果全部返回。这种方式存在两个致命问题:

  • 阻塞服务:Redis 是单线程模型,keys命令执行期间会占用主线程,导致其他请求(如读写操作)无法响应,数据量越大,阻塞时间越长(毫秒级到秒级不等);
  • 内存暴涨:若符合条件的键数量极多(如 100 万 +),keys会一次性将所有结果加载到内存,可能引发 Redis 内存溢出或 OS 级别的 Swap,严重时导致服务崩溃。

scan命令的设计理念是 **“渐进式遍历”**:将全量遍历拆分为多次小规模遍历,每次只返回少量结果(默认 10 个),既不会阻塞主线程,也不会占用过多内存。这使得它成为生产环境中遍历键的首选方案。

二、scan 命令的基础用法:从语法到示例

2.1 核心语法

scan命令的基本格式如下:

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

各参数的含义如下:

  • cursor:游标,是scan的核心标识,用于记录遍历的 “位置”。首次遍历需传入0,后续遍历需传入上一次返回的游标值,直到游标返回0表示遍历结束;
  • MATCH pattern:可选参数,用于过滤符合指定模式的键,支持通配符*(匹配任意字符)、?(匹配单个字符)、[](匹配指定范围内的字符);
  • COUNT count:可选参数,用于指定每次遍历的 “预期返回数量”,默认值为10。注意:这只是 “预期值”,并非实际返回数量,Redis 会根据内部数据结构调整,可能多也可能少;
  • TYPE type:可选参数,用于过滤指定数据类型的键,支持stringhashlistsetzset等,Redis 6.0 + 版本支持。

2.2 基础示例:遍历所有键

假设 Redis 中存在以下键:user:100user:101order:200product:300,我们通过scan遍历所有键:

  1. 首次遍历:传入游标0,不指定MATCHCOUNT(默认返回 10 个):
127.0.0.1:6379> SCAN 0
1\) "14"  # 下一次遍历的游标
2\) 1) "user:100"
   2\) "order:200"
   3\) "product:300"

结果中,第一个元素"14"是下一次遍历的游标,第二个元素是本次返回的键列表(共 3 个,少于默认的 10 个,符合 “预期值” 特性)。

  1. 第二次遍历:传入上一次返回的游标14
127.0.0.1:6379> SCAN 14
1\) "0"  # 游标返回0,遍历结束
2\) 1) "user:101"

此时游标返回0,表示所有键已遍历完成,本次返回剩余的user:101

2.3 过滤场景:MATCH 与 TYPE 的使用

场景 1:匹配 “user:” 前缀的键

通过MATCH user:*过滤前缀为user:的键:

127.0.0.1:6379> SCAN 0 MATCH user:\*
1\) "0"
2\) 1) "user:100"
   2\) "user:101"

由于符合条件的键较少,一次遍历就完成,游标直接返回0

场景 2:只遍历 string 类型的键

假设user:100string类型,order:200hash类型,通过TYPE string过滤:

127.0.0.1:6379> SCAN 0 TYPE string
1\) "8"
2\) 1) "user:100"

三、scan 的核心原理:为什么能 “渐进式” 遍历?

要正确使用scan,必须理解其底层原理 ——基于 Redis 的哈希表结构和游标跳转

3.1 Redis 的键空间存储:哈希表

Redis 的键空间(keyspace)是通过哈希表实现的,哈希表中的每个 “桶”(bucket)存储若干个键(通过哈希冲突链解决冲突)。scan的遍历本质是遍历哈希表的桶,而非直接遍历键。

3.2 游标与 “高位进位”

scan的游标并非简单的 “桶索引”,而是基于 “高位进位” 的算法设计:

  • Redis 会为哈希表分配一个固定的 “位数”(如 16 位,对应 65536 个桶),游标是一个无符号整数,长度与哈希表位数一致;
  • 每次遍历后,游标会按照 “高位进位” 的规则生成下一个游标(例如,16 位游标0000000000000000(0)的下一个游标可能是0000000000001000(8),再下一个是0000000000010000(16)等);
  • 当游标再次回到0时,表示所有桶已遍历完成,即整个键空间遍历结束。

3.3 COUNT 参数的 “预期” 特性

为什么COUNT是 “预期值” 而非 “固定值”?原因有两点:

  • 哈希冲突:一个桶中可能存储多个键,遍历该桶时会返回所有键,导致实际数量超过COUNT
  • 空桶跳过:若某个桶中没有键,Redis 会直接跳过,导致实际数量少于COUNT

例如,设置COUNT 2,但实际返回 3 个键:

127.0.0.1:6379> SCAN 0 COUNT 2
1\) "12"
2\) 1) "user:100"
   2\) "order:200"
   3\) "product:300"  # 因哈希冲突,桶中键数量超过COUNT

四、scan 家族命令:不止遍历键

scan是一个 “家族”,除了遍历整个键空间的scan命令,还有针对特定数据类型的渐进式遍历命令,用法与scan一致,仅作用对象不同:

命令作用对象用途
HSCANHash 类型的字段遍历 Hash 中的 field-value 对
SSCANSet 类型的元素遍历 Set 中的所有元素
ZSCANSorted Set 类型的元素遍历 ZSet 中的 member-score 对

示例:HSCAN 遍历 Hash 字段

假设user:100是 Hash 类型,存储用户信息:

127.0.0.1:6379> HSET user:100 name "zhangsan" age 25 city "beijing"
(integer) 3

通过HSCAN遍历其字段:

\# 首次遍历:游标0,COUNT 2
127.0.0.1:6379> HSCAN user:100 0 COUNT 2
1\) "2"  # 下一次游标
2\) 1) "name"
   2\) "zhangsan"
   3\) "age"
   4\) "25"
\# 第二次遍历:游标2
127.0.0.1:6379> HSCAN user:100 2
1\) "0"  # 遍历结束
2\) 1) "city"
   2\) "beijing"

五、使用 scan 的注意事项与常见误区

5.1 避免 “重复” 与 “遗漏”

scan的设计目标是 “不重复、不遗漏”,但在以下场景可能出现问题:

  • 数据动态变化:遍历过程中,若键被添加、删除或修改,可能导致某个键被重复遍历或遗漏。这是渐进式遍历的固有特性,无法完全避免,需在业务层做兼容(如通过唯一 ID 去重);
  • 游标失效:若两次遍历间隔过长(如超过 Redis 的超时时间),游标可能失效,导致遍历中断。建议缩短遍历间隔,确保游标连续性。

5.2 合理设置 COUNT 参数

  • 小数据量:默认COUNT 10即可,无需调整;
  • 大数据量:若需加快遍历速度,可适当增大COUNT(如COUNT 1000),但需注意:COUNT越大,单次遍历占用的主线程时间越长,需在 “速度” 和 “阻塞风险” 间平衡,建议不超过10000

5.3 避免在遍历中使用复杂过滤

MATCHTYPE过滤是在 “遍历后” 执行的 ——Redis 会先遍历出一批键,再过滤掉不符合条件的键。若符合条件的键比例极低(如MATCH user:100000*,但实际只有 1 个),会导致大量无效遍历,浪费资源。此时建议在业务层过滤,或通过 Redis 的键命名规范减少无效匹配。

5.4 不要依赖返回结果的顺序

scan的返回结果是基于哈希表的桶顺序,与键的插入顺序无关,且每次遍历的顺序可能不同。业务层不应依赖scan的返回顺序。

到此这篇关于Redis中渐进式命令scan详解与使用的文章就介绍到这了,更多相关Redis 渐进式命令scan内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • windows环境下Redis+Spring缓存实例讲解

    windows环境下Redis+Spring缓存实例讲解

    这篇文章主要为大家详细介绍了windows环境下Redis+Spring缓存实例教程,感兴趣的小伙伴们可以参考一下
    2016-04-04
  • RediSearch加RedisJSON大于Elasticsearch的搜索存储引擎

    RediSearch加RedisJSON大于Elasticsearch的搜索存储引擎

    这篇文章主要为大家介绍了RediSearch加RedisJSON大于Elasticsearch的王炸使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-07-07
  • Redis中缓存预热与缓存穿透解决方案

    Redis中缓存预热与缓存穿透解决方案

    Redis缓存预热与缓存穿透是Redis缓存使用中的两个重要概念,文章首先介绍了Redis缓存预热和缓存穿透的基本概念,然后详细阐述了它们的产生原因和解决方案,感兴趣的可以了解一下
    2023-12-12
  • redis击穿 雪崩 穿透超详细解决方案梳理

    redis击穿 雪崩 穿透超详细解决方案梳理

    这篇文章主要为大家介绍了Redis击穿穿透雪崩产生原因及解决思路的解决方案参考,有需要的朋友可以借鉴参考下,希望能够有所帮助祝大家多多进步
    2022-03-03
  • redis中hiredis-API函数的调用方法

    redis中hiredis-API函数的调用方法

    这篇文章主要介绍了redis中hiredis-API函数的调用,本文通过示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2023-09-09
  • IDEA中的Redis插件连接Redis服务器

    IDEA中的Redis插件连接Redis服务器

    本文主要介绍了IDEA中的Redis插件连接Redis服务器,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-02-02
  • 查看redis的缓存时间方式

    查看redis的缓存时间方式

    这篇文章主要介绍了查看redis的缓存时间方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-03-03
  • 详解三分钟快速搭建分布式高可用的Redis集群

    详解三分钟快速搭建分布式高可用的Redis集群

    这篇文章主要介绍了详解三分钟快速搭建分布式高可用的Redis集群,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-02-02
  • 一文带你深入理解Redis的主从架构

    一文带你深入理解Redis的主从架构

    Redis主从架构是一种分布式数据库架构,它包括一个主节点(Master)和一个或多个从节点(Slave),主节点处理所有写操作,从节点负责复制主节点的数据并处理读请求,本文将带大家深入理解Redis主从架构,需要的朋友可以参考下
    2023-09-09
  • 线上Redis一直报连接超时该如何解决

    线上Redis一直报连接超时该如何解决

    这篇文章主要为大家详细介绍了项目开发时如果出现线上Redis一直报连接超时的问题该如何解决,文中的示例代码简洁易懂,需要的小伙伴可以借鉴一下
    2023-08-08

最新评论