SpringBoot中RedisCluster的scan命令实践

为什么要用 scan 获取key

redis作为缓存服务应用非常广泛,保证应用有较强的响应性能缓存是会大量使用的,同时也就造成了redis中的缓存key非常之多,集群中几十万key更是常有,由于redis是单线程模型应用(redis并不是单线程,只是某个阶段只有一个线程,命令接收、命令处理、结果响应都是各一个线程)一个复杂操作直接会阻塞后续命令的执行,于是诞生了scan命令。

什么场景使用

对缓存key进行批量过期但是无法知道具体的key值,此时可以使用scan扫描出具体key后进行统一过期。

如何使用

之前在网上找了很多关于springboot reidsTemplete的scan操作代码,遗憾的是有很多操作都是无法在集群环境进行scan操作的。

上面咱们有有说到springboot2.0后使用lettuce作为redis连接工具,lettuce十分强大并且已经实现RedisCluster的scan操作(底层实现是逐个扫描分片),已阅读源代码,同时支持单机、主从及集群模式。

直接上代码

Set<String> keys = redisTemplate.execute((RedisConnection connection) -> {
    Set<String> keySet = CollUtil.newHashSet();
    //定义起始游标,获取lettuce原生引用,定义scan参数
    ScanCursor scanCursor = ScanCursor.INITIAL;
    RedisKeyAsyncCommands commands = (RedisKeyAsyncCommands) connection.getNativeConnection();
    ScanArgs scanArgs = ScanArgs.Builder.limit(1000).match(patternKey);
    try {
        do {
            //最少scan一次,当返回不为空时将扫描到的key添加到统一key列表中
            KeyScanCursor<byte[]> keyScanCursor = (KeyScanCursor) commands.scan(scanCursor, scanArgs).get();
            if (keyScanCursor != null) {
                if (CollUtil.isNotEmpty(keyScanCursor.getKeys())) {
                    keyScanCursor.getKeys().forEach(b -> keySet.add(new String(b)));
                }
                scanCursor = keyScanCursor;
            } else {
                scanCursor = ScanCursor.FINISHED;
            }
        } while (!scanCursor.isFinished());
    } catch (Exception e) {
        LOG.error("redisClient scanKey fail patternKey:[{}]", patternKey, e);
    }
    return keySet;
});
上一篇:记生产环境 rabbitmq 部分客户端 channel 持续积压消息不进行ack


下一篇:社交系统中用户好友关系数据库设计