RedisTemplate 实现 scan 方法

问题来源: 工作中遇到一个问题,需要清理大量的 key ,由于数量过于大,用 keys 获取时可能会造成 redis 的阻塞,所以就想到用 scan 命令。scan 命令对于集群来说只能获取到单台机器的数据,所以对集群上的所有机器都执行 scan 命令。公司用的 spring-boot 所依赖的 spring-redis-data 中 RedisTemplate 并没有 scan 方法,也在网上查了一些实现,不是只获取了单台机器的数据,就是无法正常返回数据,实现起来也并不简单。

解决思路: redis 的命令并不只有 scan 命令只能在单台机器上执行,keys 其实也是只能在单台机器上执行,解决思路就清晰了,直接去看一下 RedisTemlate 中 keys 方法是如何实现的,仿写一下就好了。

	// spring-redis-data version: 2.2.x
	/*
	 * (non-Javadoc)
	 * @see org.springframework.data.redis.core.RedisOperations#keys(java.lang.Object)
	 */
	@Override
	@SuppressWarnings("unchecked")
	public Set<K> keys(K pattern) {

		byte[] rawKey = rawKey(pattern);
		Set<byte[]> rawKeys = execute(connection -> connection.keys(rawKey), true);

		return keySerializer != null ? SerializationUtils.deserialize(rawKeys, keySerializer) : (Set<K>) rawKeys;
	}

于是产生了如下代码:

	/*
	* @Resource
	* RedisTemplate redisTemplate;
	*/
	public Set<String> scan (String pattern, int count) {
		Set<String> keys = new HashSet<>();
		RedisSerializer serializer = redisTemplate.getKeySerializer();
		ScanOptions scanOptions = ScanOptions.scanOptions().match(pattern).count(count).build();
		Cursor<byte[]> cursor = redisTemplate.execute(connection -> connection.scan(scanOptions), true);
		while (cursor.hasNext()) {
			keys.add(String.valueOf(serializer.deserialize(cursor.next())));
		}
		return keys;
	}

测试过后跟通过 keys 方法得到的结果集对比,发现是一样的,说明这样写是可行的。可通过调节 count 的大小来控制单次 scan 命令扫描的数据量,redis 中默认是 10 。注意:不要把 count 的值设成 Integer.MAX_VALUE 或过大,否则会产生和 keys 命令类似的效果,可能会阻塞 redis。

最后放上最新版 spring-redis-data 中的 scan 方法实现(不禁发出感叹,如果早四个月,没准就是我贡献了这部分代码了,哈哈哈)

	@Override
	public Cursor<K> scan(ScanOptions options) {
		Assert.notNull(options, "ScanOptions must not be null");

		return executeWithStickyConnection(
				(RedisCallback<Cursor<K>>) connection -> new ConvertingCursor<>(connection.scan(options),
						this::deserializeKey));
	}

	@SuppressWarnings("unchecked")
	private K deserializeKey(byte[] value) {
		return keySerializer != null ? (K) keySerializer.deserialize(value) : (K) value;
	}
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐