欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 艺术 > Redis SCAN 命令的详细介绍

Redis SCAN 命令的详细介绍

2025/4/24 2:16:16 来源:https://blog.csdn.net/weixin_46004749/article/details/147384392  浏览:    关键词:Redis SCAN 命令的详细介绍

Redis SCAN 命令的详细介绍

以下是 Redis SCAN​ 命令的详细介绍,结合其核心特性、使用场景及底层原理进行综合说明:

工作原理图 :


Editor _ Mermaid Chart-2025-04-21-022556


一、核心特性

  1. 非阻塞式迭代

    • 通过游标(Cursor) 分批次遍历键,避免一次性全量扫描阻塞主线程。
    • 每次迭代仅返回少量数据(默认约 10 个键),分散服务器压力。
  2. 弱一致性保证

    • 迭代过程中若键被修改(新增/删除),可能导致重复或遗漏。
    • 采用快照机制,但无法保证强一致性,需业务层处理重复数据。
  3. 支持模式匹配与类型过滤

    • MATCH​​ 参数支持通配符(如 user:*​)过滤键名。
    • TYPE​ 参数(Redis 6.0+)可指定键类型(如 hash​​、string​​)。

二、命令语法与参数

SCAN cursor [MATCH pattern] [COUNT count] [TYPE type]
  • **cursor​**
    初始值为 0​,后续使用前次返回的新游标。当游标返回 0​ 时,迭代结束。
  • **COUNT​**
    建议单次返回的键数量(默认 10),但实际结果可能多于或少于该值。
    (例:COUNT 1000提示 Redis 尝试每批返回约 1000 个键)

三、底层原理

  1. 高位进位加法遍历

    • 通过二进制高位进位顺序遍历字典槽(Slot),避免扩容/缩容导致的数据遗漏或重复。
    • 例如:从 0000​ → 1000​ → 0100​ → 1100​,确保新旧哈希表遍历顺序连续。
  2. 字典扩容与渐进式 Rehash

    • 扩容时新旧哈希表共存,SCAN​ 会同时遍历两个表,保证数据完整性。
    • 缩容可能导致部分键被重复扫描,需客户端去重。

四、使用场景

  1. 生产环境大数据量遍历

    • 替代 KEYS​ 命令,避免因全量扫描导致服务阻塞
    • 示例:遍历百万级用户会话键(session:*​)进行清理。
  2. 数据结构专用迭代

    • SSCAN​(集合)、HSCAN​(哈希)、ZSCAN​(有序集合)支持按类型迭代元素。
  3. 模糊查询与分页

    • 结合 MATCH​ 实现模糊匹配,利用 COUNT​ 近似分页控制返回量。

五、注意事项

  1. 重复键处理

    • 迭代期间键空间变动可能导致重复结果,需客户端去重。
  2. COUNT 参数优化

    • 根据数据规模调整 COUNT​ 值(如 1000~10000),平衡网络往返次数与单次负载。
  3. 弱一致性的影响

    • 不适用于需精确统计的场景(如实时计数),建议改用其他方案(如维护索引集合)。

使用案例:

从redis中取出数据同步到后台的其他持久化数据库 demo 这种分批扫描的方法可以避免一次返回大量 key 而导致 Redis 阻塞,同时可以根据需要对每批数据进行处理

package com.example.scan;/*** 描述: 从 Redis 批量获取暂存数据并持久化到数据库。通过SCAN分批拉取数据,确保系统稳定性* 1. 循环获取指定开头的key* 2. 判断key 的数据类型* 3. 对不同的数据类型做相应的处理* 4. 这里模拟如果是string 同步到数据库 其他只是简单的打印 后续可以根据业务场景的不通 做不同的处理* @author ZHOUXIAOYUE* @date 2025/4/21 10:20*/import redis.clients.jedis.Jedis;
import redis.clients.jedis.ScanParams;
import redis.clients.jedis.ScanResult;import java.util.List;
import java.util.Map;
import java.util.Set;public class RedisDataMigration {public static void main(String[] args) {// 初始化 Jedis 客户端Jedis jedis = new Jedis("localhost", 6379);// SCAN 命令参数设置// count 参数表示每次扫描大概处理多少条数据,这里设置为 100ScanParams scanParams = new ScanParams().match("user:*").count(100);// 如果需要,可以通过 match 设置键模式// scanParams.match("temp:*");String cursor = "0";do {// 执行 SCAN 命令ScanResult<String> scanResult = jedis.scan(cursor, scanParams);List<String> keys = scanResult.getResult();cursor = scanResult.getCursor();// 模拟持久化到数据库的操作keys.forEach(key -> {// 获取 key 的数据类型String type = jedis.type(key);System.out.println("Processing key: " + key + ",类型为:" + type);switch (type) {case "string":// 如果是字符串类型,直接调用 get 方法String strValue = jedis.get(key);System.out.println("String value: " + strValue);persistDataToDB(key, strValue);break;case "list":// 如果是列表类型,通过 lrange 获取所有列表元素List<String> listValue = jedis.lrange(key, 0, -1);System.out.println("List value: " + listValue);break;case "set":// 如果是集合类型,通过 smembers 获取所有成员Set<String> setValue = jedis.smembers(key);System.out.println("Set value: " + setValue);break;case "zset":// 如果是有序集合类型,通过 zrange 获取所有元素(默认按分数从小到大排序)Set<String> zsetValue = jedis.zrange(key, 0, -1);System.out.println("ZSet value: " + zsetValue);break;case "hash":// 如果是 Hash 类型,通过 hgetAll 获取所有键值对Map<String, String> hashValue = jedis.hgetAll(key);System.out.println("Hash value: " + hashValue);break;default:// 对于未知类型或其他类型的值,可以在这里处理System.out.println("Unknown type for key: " + key);break;}});// 可以适当休眠,避免对 Redis 服务器产生太大压力try {Thread.sleep(100);} catch (InterruptedException e) {Thread.currentThread().interrupt();System.out.println("Interrupted: " + e.getMessage());}} while (!"0".equals(cursor)); // cursor 为 "0" 时表示遍历结束jedis.close();System.out.println("数据迁移完成。");}/*** 模拟持久化数据到数据库** @param key   Redis 的键* @param value Redis 的值*/private static void persistDataToDB(String key, String value) {// 此处仅作模拟,可替换为真实的数据库持久化操作System.out.println("持久化数据 - key: " + key + ", value: " + value);// 例如:// myDatabase.save(new DataEntity(key, value));}
}


总结

场景推荐方案避免方案
生产环境遍历海量键SCAN​+ 合理COUNT​值KEYS​命令
精确统计或强一致性需求维护索引集合/Lua 脚本依赖SCAN​结果
分页查询SCAN​+MATCH​+COUNT单次全量加载

最佳实践

  • 优先使用 SCAN​ 替代 KEYS​,并在客户端实现去重逻辑。
  • 结合 TYPE​ 参数(Redis 6.0+)减少无效遍历。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词