Java 使用 lettuce-core 批量扫描与删除 Redis 键:单节点与集群实战

在 Java 生态中,Redis 客户端的选择至关重要。lettuce-core 是一款高性能、基于 Netty 的非阻塞 Redis 客户端,目前已被 Spring Boot 作为默认的 Redis 客户端。相比于传统的 Jedis,lettuce-core 采用了更底层的 Netty 框架,原生支持异步、响应式编程模型,并且在连接池管理、线程安全性以及集群拓扑自动刷新方面表现更为优异。对于需要高并发处理或复杂集群运维的场景,lettuce-core 无疑是更现代、更可靠的选择。

本文将结合实际业务场景,演示如何使用 lettuce-core 在单节点和集群模式下,安全地批量扫描并删除指定模式的键。

引入 lettuce-core 依赖

首先,我们需要在项目中引入 lettuce-core 依赖。以下是当前推荐的稳定版本配置:

<!-- lettuce-core 官方依赖,提供高性能的 Redis 客户端支持 -->
<dependency>
  <groupId>io.lettuce</groupId>
  <artifactId>lettuce-core</artifactId>
  <version>6.5.5.RELEASE</version>
</dependency>

集群模式:批量扫描与删除

在 Redis 集群模式下,键分布在不同的节点上,因此需要使用 RedisClusterClient 和 RedisAdvancedClusterCommands 来执行跨节点的扫描操作。

以下代码展示了如何安全地遍历并删除匹配 sit_eds:* 模式的键。这里使用了 SCAN 命令而非 KEYS 命令,以避免在生产环境中阻塞 Redis 服务。

@Test
public void clearAllSIT() {
    // 配置 Redis 认证信息,使用占位符替代真实密码
    RedisCredentialsProvider credentialsProvider = new StaticCredentialsProvider("default", "YOUR_PASSWORD".toCharArray());
    RedisURI redisURI = RedisURI.create("redis://10.100.242.186:6379");
    redisURI.setCredentialsProvider(credentialsProvider);
    
    // 创建集群客户端并建立连接
    RedisClusterClient clusterClient = RedisClusterClient.create(redisURI);
    try (StatefulRedisClusterConnection<String, String> connect = clusterClient.connect()) {
        RedisAdvancedClusterCommands<String, String> sync = connect.sync();

        // 配置扫描参数:匹配模式与单次迭代数量限制
        ScanArgs scanArgs = ScanArgs.Builder
                .matches("sit_eds:*")
                .limit(100); // 注意:limit 控制的是每次迭代返回的键数量,而非总数

        // 初始化游标,开始循环扫描
        ScanCursor scanCursor = ScanCursor.INITIAL;
        int count = 0;
        do {
            // 执行扫描,获取当前批次的键
            KeyScanCursor<String> keyScanCursor = sync.scan(scanCursor, scanArgs);
            List<String> keys = keyScanCursor.getKeys();
            for (String key : keys) {
                log.info("found key: {}", key);
                count++;
                sync.del(key);// 删除key
            }
            // 更新游标,用于下一轮迭代
            scanCursor = keyScanCursor;
        } while (!scanCursor.isFinished());

        log.info("扫描完成,匹配 sit_eds:* 的key总数:{}", count);
    } finally {
        // 确保客户端资源被正确释放
        clusterClient.shutdown();
    }
}

技术解析:SCAN 游标机制

在上述代码中,SCAN 命令的核心在于游标(Cursor)机制。与 KEYS * 一次性返回所有结果不同,SCAN 采用增量式迭代:

  1. 服务端维护一个游标状态,每次调用 SCAN 只返回一小部分匹配的键和下一个游标值。
  2. 客户端拿着新的游标继续请求,直到服务端返回的游标值为 0(即 isFinished() 为 true),表示遍历完成。
  3. 这种机制将一次大操作拆分为多次小操作,极大降低了对 Redis 主线程的阻塞风险,是生产环境批量处理键的唯一推荐方式。

单节点模式:适配与调整

如果你的 Redis 是单节点部署,代码逻辑基本一致,但需要替换客户端和连接类型。主要变更点如下:

  • RedisClusterClient 替换为 RedisClient
  • StatefulRedisClusterConnection 替换为 StatefulRedisConnection
  • RedisAdvancedClusterCommands 替换为 RedisCommands

以下是单节点模式的完整实现:

@Test
public void clearAllSIT() {
    // 配置认证信息,密码请使用占位符
    RedisCredentialsProvider credentialsProvider = new StaticCredentialsProvider("default", "YOUR_PASSWORD".toCharArray());
    RedisURI redisURI = RedisURI.create("redis://10.100.242.186:6379");
    redisURI.setCredentialsProvider(credentialsProvider);
    
    // 创建单节点客户端
    RedisClient redisClient = RedisClient.create(redisURI);
    try (StatefulRedisConnection<String, String> connect = redisClient.connect()) {
        RedisCommands<String, String> sync = connect.sync();

        // 扫描参数配置
        ScanArgs scanArgs = ScanArgs.Builder
                .matches("sit_eds:*")
                .limit(100); // 单次迭代数量限制

        // 游标迭代逻辑
        ScanCursor scanCursor = ScanCursor.INITIAL;
        int count = 0;
        do {
            KeyScanCursor<String> keyScanCursor = sync.scan(scanCursor, scanArgs);
            List<String> keys = keyScanCursor.getKeys();
            for (String key : keys) {
                log.info("found key: {}", key);
                count++;
                sync.del(key);// 删除key
            }
            // 更新游标
            scanCursor = keyScanCursor;
        } while (!scanCursor.isFinished());

        log.info("扫描完成,匹配 sit_eds:* 的key总数:{}", count);
    } finally {
        // 释放单节点客户端资源
        redisClient.shutdown();
    }
}

总结与注意事项

使用 lettuce-core 进行 Redis 键的批量管理时,请务必注意以下几点:

  1. 严禁在生产环境使用 KEYS 命令:KEYS 命令的时间复杂度为 O(N),在键数量庞大时会导致 Redis 瞬间无响应。始终使用 SCAN 系列命令。
  2. 合理设置 limit 参数:ScanArgs.limit() 并不是精确的返回数量,Redis 不保证每次严格返回该数量的键,但它能有效控制单次网络请求和 CPU 处理的开销。建议设置在 50-200 之间。
  3. 资源释放:lettuce-core 的连接和客户端是非阻塞的,但依然需要确保在 finally 块中调用 shutdown() 方法,防止连接泄漏。
  4. 敏感信息安全:在代码提交或分享时,务必将密码替换为环境变量或配置中心读取的方式,切勿硬编码真实密码。