SpringBoot中RedisCluster的scan命令如何使用?

线上环境redis key过期一直很头疼,自动过期用户可能会给用户展示过期数据无法忍受,可是又无法掌握固定的key,redis给我们提供了高性能的scan操作,可千万不能用keys * 了!
SpringBoot2.0升级使用lettuce替换了jedis为默认的reids连接工具。

为什么要用 scan 获取key

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

什么场景使用

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

如何使用

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

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

直接上代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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;
});