为什么Java通过redisTemplate使用Pipeline批量查询拿到的值都是空?
redisTemplate使用Pipeline批量查询,返回的结果全是null,keys集合是有数据的,redis里也是有数据的
public <T> List<T> batchGetList(Collection<String> keys) {
List<T> list = new ArrayList<>();
if (CollectionUtil.isEmpty(keys)) {
return list;
}
// 自定义序列化
RedisSerializer stringSerializer = redisTemplate.getStringSerializer();
RedisSerializer valueSerializer = redisTemplate.getValueSerializer();
RedisSerializer defaultSerializer = redisTemplate.getDefaultSerializer();
List result = redisTemplate.executePipelined(new RedisCallback<Object>() {
@Override
public Object doInRedis(RedisConnection connection) throws DataAccessException {
for (String k : keys) {
System.err.println(k);
byte[] bytes = connection.get(stringSerializer.serialize(k));
byte[] bytes1 = connection.get(defaultSerializer.serialize(k));
byte[] bytes2 = connection.get(valueSerializer.serialize(k));
System.err.println(Arrays.toString(bytes));
System.err.println(Arrays.toString(bytes1));
System.err.println(Arrays.toString(bytes2));
Object deserialize = defaultSerializer.deserialize(bytes);
System.err.println(deserialize);
list.add((T) deserialize);
}
return null;
}
}, redisTemplate.getValueSerializer());
return list;
}
换了一种方式,返回还是null
public <T> List<T> batchGetList1(Collection<String> keys) {
List<T> list = new ArrayList<>();
if (CollectionUtil.isEmpty(keys)) {
return list;
}
List result = redisTemplate.executePipelined(new SessionCallback<Object>() {
@Override
public Object execute(RedisOperations operations) throws DataAccessException {
keys.forEach(key->{
System.err.println(key);
Object o = operations.opsForValue().get(key);
System.err.println(o);
list.add((T) o);
});
return null;
}
});
return list;
}
回复
1个回答

test
2024-06-20
在使用Spring Data Redis的RedisTemplate进行批量操作时,如果遇到使用管道(Pipeline)查询但返回的结果全是null,同时确认keys集合中确实有数据并且Redis中也有对应的数据,这通常是由于对管道操作的结果处理不当导致的。
首先,需要明白在使用管道技术时,Redis命令的执行结果不会立即返回。所有的命令都会被缓存起来,直到调用executePipelined方法时才一次性发送给Redis服务器,而executePipelined方法本身会返回一个包含每个命令响应的List。这就是为什么你的第一个示例方法中尝试在管道操作内部处理命令结果(例如直接反序列化)不会奏效的原因。管道操作完成后,你应该处理executePipelined返回的结果列表。
此外,你的第一个方法示例中尝试在循环内对每个键进行获取并反序列化,这实际上违背了使用管道的初衷,因为这样做并没有减少网络往返次数,而且实际上doInRedis方法的返回值应该被用来处理所有命令的结果。
对于第二个示例,正确的做法是在executePipelined之后处理结果,而不是在管道操作内部。
public <T> List<T> batchGetList(Collection<String> keys) {
if (CollectionUtil.isEmpty(keys)) {
return new ArrayList<>();
}
List<Object> results = redisTemplate.executePipelined((RedisConnection connection) -> {
RedisSerializer<String> keySerializer = (RedisSerializer<String>) redisTemplate.getKeySerializer();
for (String key : keys) {
connection.get(keySerializer.serialize(key));
}
return null;
});
// 在管道执行外部处理结果
return results.stream()
.map(result -> (T) redisTemplate.getValueSerializer().deserialize((byte[]) result))
.collect(Collectors.toList());
}
这个方法发送所有的GET命令作为一个批量操作,并在操作完成后一次性处理所有的结果。请注意,这里假设所有键对应的值都是可以使用相同的反序列化器反序列化的。如果你的应用场景中有不同类型的值,你可能需要对结果进行进一步的处理来正确地反序列化它们。
回复

适合作为回答的
- 经过验证的有效解决办法
- 自己的经验指引,对解决问题有帮助
- 遵循 Markdown 语法排版,代码语义正确
不该作为回答的
- 询问内容细节或回复楼层
- 与题目无关的内容
- “赞”“顶”“同问”“看手册”“解决了没”等毫无意义的内容