Redis Scan 命令使用教程(高效遍历海量数据的方法)

 更新时间:2025年09月11日 14:19:02   作者:不想写bug的小滕  
Scan 是 Redis 2.8 版本引入的迭代式遍历命令,主要用于遍历 Redis 中的键集合或集合类型(如 Hash、Set、Sorted Set)的元素,本文将从基础介绍、实现原理、实操方式到实践总结,全面讲解 Scan 命令的使用,感兴趣的朋友跟随小编一起看看吧

Redis 中 Scan 命令使用教程:高效遍历海量数据

在 Redis 中,当需要遍历所有键或指定模式的键时,传统的 KEYS 命令会因阻塞主线程、无法分页等问题,在海量数据场景下表现糟糕。而 Scan 命令凭借 “非阻塞”“分批遍历” 的特性,成为解决大规模数据遍历的最优方案。本文将从基础介绍、实现原理、实操方式到实践总结,全面讲解 Scan 命令的使用。

一、介绍

Scan 是 Redis 2.8 版本引入的迭代式遍历命令,主要用于遍历 Redis 中的键集合或集合类型(如 Hash、Set、Sorted Set)的元素,核心目标是解决传统遍历命令的性能问题。

1. 为什么需要 Scan?

传统的 KEYS 命令存在明显缺陷:

  • 阻塞主线程KEYS 会一次性遍历所有符合条件的键,若数据量达百万级,会占用大量 CPU 时间,导致 Redis 无法响应其他请求;
  • 无分页能力KEYS 只能一次性返回所有结果,无法分批处理,容易造成客户端内存溢出;
  • 不支持复杂筛选:仅能通过简单的通配符(如 *?)匹配,灵活性低。

Scan 命令恰好弥补这些不足,具备以下核心特性:

  • 非阻塞:分批遍历数据,每次只处理少量元素,避免长时间占用主线程;
  • 游标迭代:通过 “游标(cursor)” 记录遍历位置,支持断点续传;
  • 安全遍历:遍历过程中数据的新增、删除、修改不会导致漏遍历或重复遍历(存在极小概率重复,但可通过业务层去重解决);
  • 多类型支持:除了遍历所有键(SCAN),还支持遍历 Hash 字段(HSCAN)、Set 元素(SSCAN)、Sorted Set 元素(ZSCAN)。

2. 典型应用场景

  • 海量键统计:如统计 Redis 中所有以 user: 为前缀的键数量;
  • 数据清理:分批删除过期或无用的键(如删除所有 temp: 前缀的临时键);
  • 集合元素遍历:遍历大型 Hash 中的所有字段值,避免一次性加载导致内存溢出;
  • 定期数据校验:分批检查键的过期时间或数据完整性。

二、使用原理

Scan 命令的核心是基于 “游标” 和 “哈希表遍历” 实现的,理解其底层原理能帮助更好地使用命令。

1. Redis 键空间的存储结构

Redis 的键空间(keyspace)底层基于哈希表存储,每个键通过哈希函数映射到哈希表的某个 “桶(bucket)” 中。Scan 命令本质是遍历哈希表的桶,并通过游标记录当前遍历到的桶位置。

2. 游标迭代机制

Scan 的遍历过程类似 “翻书”,游标就是 “页码”,具体流程如下:

  • 初始游标:首次调用 Scan 时,游标值设为 0,表示从哈希表的起始位置开始遍历;
  • 分批遍历:Redis 会根据游标位置,返回当前批次的元素(默认 10 个),并返回新的游标值;
  • 结束条件:当返回的游标值为 0 时,表示已遍历完所有元素;
  • 断点续传:若遍历中断(如客户端重启),下次可使用上次返回的非 0 游标继续遍历,无需从头开始。

3. 避免漏遍历与重复遍历的设计

由于 Redis 哈希表在扩容(rehash)时会重新分配桶的位置,Scan 通过以下机制保证遍历的准确性:

  • 渐进式 rehash 兼容:遍历过程中若触发哈希表扩容,Scan 会同时遍历旧哈希表和新哈希表,确保所有键都能被访问到;
  • 允许重复遍历:为了简化实现,Scan 不保证元素只出现一次(尤其是在 rehash 过程中),但重复概率极低,业务层可通过去重(如用 Set 暂存结果)解决。

4. 计数参数(count)的作用

Scan 命令中的 count 参数用于指定 “每次遍历的桶数量”,而非 “返回的元素数量”:

  • 默认 count=10,表示每次遍历 10 个桶,返回这些桶中的所有符合条件的元素;
  • count 并非严格限制,Redis 会根据桶中元素数量动态调整返回结果(如某个桶中没有符合条件的键,可能返回少于 count 个元素);
  • 海量数据场景下,可适当增大 count(如 count=1000),减少遍历次数,提升效率。

三、使用方式

Scan 命令家族包括 SCAN(遍历键空间)、HSCAN(遍历 Hash)、SSCAN(遍历 Set)、ZSCAN(遍历 Sorted Set),核心用法类似,以下以最常用的 SCAN 为例讲解,其他命令用法可类比。

1. 基础语法

(1)SCAN 命令(遍历所有键)
 语法:SCAN cursor [MATCH pattern]  [COUNT count]  [TYPE type]
cursor:游标值(首次为 0,后续用上次返回的游标)
MATCH pattern:通配符匹配,筛选符合条件的键(可选)
COUNT count:每次遍历的桶数量(可选,默认 10)
TYPE type:按键类型筛选(如 string、hash、set,可选,Redis 6.0+ 支持)
(2)其他命令语法(类比)
  • HSCAN key cursor [MATCH pattern] [COUNT count]:遍历 Hash 键 key 的字段和值;
  • SSCAN key cursor [MATCH pattern] [COUNT count]:遍历 Set 键 key 的元素;
  • ZSCAN key cursor [MATCH pattern] [COUNT count]:遍历 Sorted Set 键 key 的元素和分数。

2. 实操示例

(1)遍历所有键(无筛选)
# 首次遍历:游标 0,默认 count=10
127.0.0.1:6379> SCAN 0
1) "17"  # 下次遍历的游标
2) 1) "user:1001"
    2) "product:2003"
    3) "order:5001"
 # 本次返回 3 个键(少于 count=10,因部分桶无符合条件的键)
# 第二次遍历:使用上次返回的游标 17
127.0.0.1:6379> SCAN 17
1) "0"  # 游标为 0,遍历结束
2) 1) "user:1002"
   2) "temp:3001"
(2)按前缀筛选键(MATCH)
# 遍历所有以 "user:" 为前缀的键,count=20
127.0.0.1:6379> SCAN 0 MATCH user:* COUNT 20
1) "23"
2) 1) "user:1001"
    2) "user:1002"
    3) "user:1003"
# 继续遍历,直到游标返回 0
127.0.0.1:6379> SCAN 23 MATCH user:* COUNT 20
1) "0"
2) 1) "user:1004"
(3)按类型筛选键(TYPE,Redis 6.0+)
# 遍历所有 string 类型的键
127.0.0.1:6379> SCAN 0 TYPE string
1) "12"
2) 1) "user:1001"  # 假设该键是 string 类型
    2) "temp:3001"
(4)遍历 Hash 键(HSCAN)
# 先创建一个 Hash 键
127.0.0.1:6379> HMSET user:1001 name "Alice" age "25" city "Beijing"
OK
# 遍历该 Hash 的字段和值
127.0.0.1:6379> HSCAN user:1001 0
1) "0"  # 游标为 0,Hash 元素少,一次遍历完
2) 1) "name"
   2) "Alice"
  3) "age"
  4) "25"
  5) "city"
  6) "Beijing"

3. 代码示例(Golang)

以遍历所有 user: 前缀的键为例,使用 go-redis 客户端实现:

package main
import (
	"context"
	"fmt"
	"github.com/go-redis/redis/v8"
)
func main() {
	// 初始化 Redis 客户端
	client := redis.NewClient(&redis.Options{
		Addr:     "localhost:6379",
		Password: "",
		DB:       0,
	})
	defer client.Close()
	ctx := context.Background()
	// Scan 遍历所有 user: 前缀的键
	var cursor uint64 = 0  // 初始游标
	count := int64(20)     // 每次遍历的桶数量
	pattern := "user:*"    // 匹配模式
	fmt.Println("开始遍历 user: 前缀的键:")
	for {
		// 执行 Scan 命令
		result, err := client.Scan(ctx, cursor, pattern, count).Result()
		if err != nil {
			fmt.Printf("Scan 执行失败:%v\n", err)
			return
		}
		// 获取本次结果和下次游标
		cursor = result.Cursor
		keys := result.Keys
		// 处理本次获取的键
		for _, key := range keys {
			fmt.Printf("找到键:%s\n", key)
		}
		// 游标为 0,遍历结束
		if cursor == 0 {
			break
		}
	}
	fmt.Println("遍历完成")
}

4. 注意事项

  • 游标必须正确传递:每次遍历需使用上次返回的游标,否则会导致重复遍历或漏遍历;
  • 避免过度依赖 MATCHMATCH 是在遍历结果中筛选,而非提前过滤,若符合条件的键极少,会导致多次空遍历,建议结合业务场景优化匹配模式;
  • count 参数按需调整:数据量小时用默认 count=10 即可,海量数据时可增大 count(如 1000~10000),但不宜过大(避免单次操作耗时过长);
  • 业务层去重:因 Scan 可能返回重复元素,需在业务层通过 Set 或哈希表去重;
  • 不建议在主库高频使用:虽然 Scan 非阻塞,但频繁遍历仍会占用 CPU 资源,建议在从库执行(需确保主从数据同步及时)。

四、总结

Scan 命令是 Redis 中处理海量数据遍历的核心工具,其核心优势可总结为:

  • 非阻塞设计:分批遍历避免阻塞主线程,保障 Redis 服务可用性;
  • 游标迭代:支持断点续传,适合长时间、大规模的遍历任务;
  • 灵活筛选:通过 MATCHTYPE 实现精准筛选,满足多样化需求;
  • 多类型支持:覆盖键空间和所有集合类型,适用场景广泛。

在实际使用中,需注意以下关键要点:

  • 正确传递游标,确保遍历的连续性;
  • 根据数据量调整 count 参数,平衡遍历效率和资源占用;
  • 结合业务场景优化筛选逻辑,减少无效遍历;
  • 对遍历结果进行去重,避免重复处理。

总之,Scan 命令彻底解决了传统 KEYS 命令的性能痛点,是 Redis 运维和开发中处理海量数据的 “必备工具”,掌握其用法能显著提升大规模 Redis 集群的管理效率。

到此这篇关于Redis Scan 命令使用教程(高效遍历海量数据的方法)的文章就介绍到这了,更多相关Redis Scan 命令使用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Redis内部数据结构Dict的实现方法

    Redis内部数据结构Dict的实现方法

    这篇文章主要介绍了Redis内部数据结构Dict的实现方法,本篇文章所述的dict在Redis中最主要的作用就是用于维护Redis数据库中所有Key、value映射的数据结构,需要的朋友可以参考下
    2022-05-05
  • redis初学者常见字符乱码问题及解决方案

    redis初学者常见字符乱码问题及解决方案

    这篇文章主要介绍了redis初学者常见字符乱码问题及解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-09-09
  • 高并发技巧之Redis和本地缓存使用技巧分享

    高并发技巧之Redis和本地缓存使用技巧分享

    在这篇文章中,我主要介绍的是分布式缓存和本地缓存的使用技巧,包括缓存种类介绍,各种的使用场景,以及如何使用,最后再给出实战案例,需要的可以参考一下
    2022-10-10
  • redis实现排行榜的简单方法

    redis实现排行榜的简单方法

    这篇文章主要给大家介绍了关于redis实现排行榜的简单方法,文中通过示例代码介绍的非常详细,对大家学习或者使用redis具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-08-08
  • Redis事务处理的使用操作方法

    Redis事务处理的使用操作方法

    Redis保证一个事务中的所有命令要么都执行,要么都不执行(原子性),如果客户端发送了EXEC命令,所有的命令就都会被执行,即使此后客户端断线也没关系,因为Redis中已经记录了所有要执行的命令,下面通过本文给大家介绍Redis事务处理的使用操作,感兴趣的朋友一起看看吧
    2021-10-10
  • Redis内存满了的几种原因和最佳解决方案

    Redis内存满了的几种原因和最佳解决方案

    Redis是一款高性能的内存数据库,被广泛应用于缓存、消息队列、计数器等场景,然而,由于Redis是基于内存的数据库,当数据量过大或者配置不合理时,就有可能导致Redis的内存满,本文将介绍Redis内存满的几种原因,并提供相应的解决方案,需要的朋友可以参考下
    2023-11-11
  • Redis锁的过期时间小于业务的执行时间如何续期

    Redis锁的过期时间小于业务的执行时间如何续期

    本文主要介绍了Redis锁的过期时间小于业务的执行时间如何续期,Redisson它能给Redis分布式锁实现过期时间自动续期,具有一定的参考价值,感兴趣的可以了解一下
    2024-05-05
  • 在Redis中如何保存时间序列数据详解

    在Redis中如何保存时间序列数据详解

    与发生时间相关的一组数据,就是时间序列数据,这些数据的特点是没有严格的关系模型,记录的信息可以表示成键和值的关系,这篇文章主要给大家介绍了关于在Redis中如何保存时间序列数据的相关资料,需要的朋友可以参考下
    2021-10-10
  • redis持久化AOF和RDB的区别及解决各个场景问题示例

    redis持久化AOF和RDB的区别及解决各个场景问题示例

    这篇文章主要为大家介绍了redis持久化AOF和RDB的区别及解决各个场景问题示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-08-08
  • Redis如何解决BigKey

    Redis如何解决BigKey

    在Redis的使用过程中,我们经常会遇到BigKey, BigKey的大值会导致Redis内存中产生大量不连续的碎片,降低内存利用效率,本文主要介绍了Redis如何解决BigKey,具有一定的参考价值,感兴趣的可以了解一下
    2024-01-01

最新评论