为什么要用 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;
});