java - Spring Data REDIS - 具有奇怪前缀的哈希键,并且 HSCAN 无法正确返回结果
问题描述
我正在使用带有 spring-data-redis:jar:2.0.9 的 spring boot(无关),它使用 lettuce 连接到我的 REDIS。我正在使用包含大约 100 个键的哈希结构。在这些键下,我放置了一些类型也无关紧要的对象:
private static final String HASH_KEY_NAME = "myspecialhashes:somekey";
@Autowired
private RedisTemplate<String, MyDto> myDtoRedisTemplate;
现在我只是将我的对象列表放入散列中,使用它们的 id 作为键:
myDtoRedisTemplate.opsForHash().put(HASH_KEY_NAME, dto.getId(), dto);
这工作正常,从哈希中检索所有元素都很好,并且只检索键
List allDtosRaw = myDtoRedisTemplate.opsForHash().values(HASH_KEY_NAME);
同样在列出键时:
myDtoRedisTemplate.boundHashOps(HASH_KEY_NAME).keys()
看起来不错,返回的键集以:
(java.util.LinkedHashSet<E>) [fakeservicetest:dummy3:write, fakesingle:dummy:sub1:write, ....
由于有很多键,我希望能够使用 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);
不幸的是,这返回零结果。我正在尝试各种方法来解决这个问题并获得一些结果。最终我转向 redis 命令行,看看 REDIS 对这一切的想法是什么
redis-cli HSCAN "myspecialhashes:somekey" 0 MATCH "fake*" COUNT 1000
确实是零结果。接下来是查看其中的所有键并查看哈希中的实际内容
redis-cli HGETALL "myspecialhashes:somekey"
结果是这样的:
1) "0"
2) 1) "\xac\xed\x00\x05t\x00\x1cfakeservicetest:dummy3:write"
2) "{\"@class\":\"
.....
看起来,这些键包含一个前缀,这些前缀是一些 Unicode 字符。这可能是由于字符串序列化(我在使用调试器将字符串放入 REDIS 之前检查了字符串,并且它们在开头不包含任何不可见字符)。所以我现在有一个可行的解决方法:我可以搜索“*fake*”,它在 REDIS CLI 和 Spring Data Redis 中都有效。因为我只想拥有那些以“fake”开头的,所以我可以在我的 Java 应用程序中使用 String.startsWith 过滤它。但是由于我不喜欢解决方法,所以我想知道如果我错误地使用了 spring data redis,或者在序列化用于放入 REDIS 的字符串和在 SCAN 中使用的字符串时是否存在一些不一致?
解决方案
好的,我现在知道了。我已经为字符串序列化程序配置了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;
}
变成
@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;
}
推荐阅读
- ios - 无法在 Swift 中找到正确的 iOS DateFormatter
- linux - 容器和主机的时差问题
- reactjs - 为什么在从事件处理程序分配状态时需要设置组件的值?
- javascript - 从服务器读取另一台机器的文件
- c# - 跨平台 C# 加密:无法生成正确的密钥/选择正确的算法
- go - 无法通过远程 ssh 命令在远程服务器上构建 go 程序
- sql - 连接来自多列和多行的字符串
- c# - 使用 WinForms 控件中的 SystemEvents 为具有静态方法的类处理模式
- python - 美式看跌期权定价的迭代方法
- freeradius - FreeRadius LDAP 组检查