Spring Data REDIS - 具有奇怪前缀的哈希键,并且 HSCAN 无法正确返回结果

mas*_*lan 5 java redis lettuce spring-data-redis

我将 spring boot (无关)与 spring-data-redis:jar:2.0.9 结合使用,它使用 lettuce 连接到我的 REDIS。我使用的哈希结构包含大约 100 个键。在这些键下,我放置了一些类型也不相关的对象:

private static final String HASH_KEY_NAME = "myspecialhashes:somekey";

@Autowired
private RedisTemplate<String, MyDto> myDtoRedisTemplate;
Run Code Online (Sandbox Code Playgroud)

现在我只是将对象列表放入哈希中,使用它们的 id 作为键:

myDtoRedisTemplate.opsForHash().put(HASH_KEY_NAME, dto.getId(), dto);
Run Code Online (Sandbox Code Playgroud)

这工作得很好,并且从哈希中检索所有元素都很好,并且仅检索键

List allDtosRaw = myDtoRedisTemplate.opsForHash().values(HASH_KEY_NAME);
Run Code Online (Sandbox Code Playgroud)

另外,在列出键时:

myDtoRedisTemplate.boundHashOps(HASH_KEY_NAME).keys()
Run Code Online (Sandbox Code Playgroud)

看起来不错,返回的密钥集以以下内容开头:

(java.util.LinkedHashSet<E>) [fakeservicetest:dummy3:write, fakesingle:dummy:sub1:write, ....
Run Code Online (Sandbox Code Playgroud)

由于有很多键,我希望能够使用 HSCAN 过滤以令牌开头的对象列表,而不是获取所有键并在我的 Java 应用程序中过滤它们。所以,这就是我如何进行 HSCAN 来获取所有以“fake”开头的哈希条目

List filteredDtosRaw = new LinkedList<>();
ScanOptions scanOptions = ScanOptions.scanOptions().match("fake*").count(10000).build();
Cursor cursor = myDtoRedisTemplate.boundHashOps(HASH_KEY_NAME).scan(scanOptions);
        cursor.forEachRemaining(filteredDtosRaw ::add);
Run Code Online (Sandbox Code Playgroud)

不幸的是,这返回零结果。我尝试了各种方法来解决这个问题并取得了一些成果。最终我转向 redis 命令行来看看 REDIS 是如何看待这一切的

redis-cli HSCAN "myspecialhashes:somekey" 0 MATCH "fake*" COUNT 1000
Run Code Online (Sandbox Code Playgroud)

确实是零结果。接下来的事情是查看其中的所有键并查看哈希中实际包含的内容

redis-cli HGETALL "myspecialhashes:somekey"
Run Code Online (Sandbox Code Playgroud)

结果是这样的:

1) "0"
2)  1) "\xac\xed\x00\x05t\x00\x1cfakeservicetest:dummy3:write"
    2) "{\"@class\":\"
.....
Run Code Online (Sandbox Code Playgroud)

因此,这些键似乎包含一些 Unicode 字符的前缀。这可能是由于字符串序列化(我在使用调试器将字符串放入 REDIS 之前检查了字符串,并且它们在开头不包含任何不可见字符)。所以我现在有一个有效的解决方法:我可以搜索“*fake*”,它在 REDIS CLI 和 Spring Data Redis 中都有效。由于我只想包含那些以“fake”开头的内容,因此我可以使用 String.startsWith 在我的 Java 应用程序中对其进行过滤。但由于我不喜欢解决方法,我想知道我是否错误地使用了 spring data redis,或者在序列化字符串以放入 REDIS 中和在 SCAN 中使用的字符串时是否存在一些不一致?

mas*_*lan 9

好吧,我现在知道了。我已经为字符串序列化器配置了 redis 序列化器,但看来哈希键需要单独设置哈希键的序列化器。那些“奇怪的 Unicode 字符”是 JdkSerializer 的结果

@Bean
public RedisTemplate<String, MyDto> redisTemplateMyDto() {
    final RedisTemplate<String, MyDto> template = new RedisTemplate<String, MyDto>();
    template.setConnectionFactory(redisConnectionFactory);
    template.setKeySerializer(new StringRedisSerializer());
    template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
    template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
    return template;
}
Run Code Online (Sandbox Code Playgroud)

变成

@Bean
public RedisTemplate<String, MyDto> redisTemplateMyDto() {
    final RedisTemplate<String, MyDto> template = new RedisTemplate<String, MyDto>();
    template.setConnectionFactory(redisConnectionFactory);
    template.setHashKeySerializer(new StringRedisSerializer());
    template.setKeySerializer(new StringRedisSerializer());
    template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
    template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
    return template;
}
Run Code Online (Sandbox Code Playgroud)