我正在使用spring数据雷迪斯与绝地武士。我正在尝试使用键vc:${list_id}
存储哈希。我能成功插入Redis。但是,当我使用redis-cli检查键时,我看不到键vc:501381
。相反,我看到的是\xac\xed\x00\x05t\x00\tvc:501381
。
为什么会发生这种情况?我该如何改变这种情况?
我知道这个问题已经有一段时间了,但是最近我又对这个话题做了一些研究,所以我想在这里通过翻阅部分spring源代码来分享一下这个“半散列”密钥是如何生成的。
首先,spring利用AOP解析诸如@cacheable、@cacheevict或@cacheput
等注释。advice类是来自spring上下文依赖的CacheInterceptor
,它是CacheAspectSupport
(也来自spring上下文)的子类。为了便于解释,我将使用@cacheable
作为示例来浏览这里的部分源代码。
当调用注释为@cacheable
的方法时,AOP会将其路由到此方法受保护的集合<?从
,它将尝试在其中解析此CacheAspectSupport
类扩展Cache>getCaches(CacheOperationInvocationContext
@Cacheable
批注。反过来,它导致在实现CacheManager中调用此方法公共缓存getCache(String name)
。对于这个解释,实现缓存的是rediscacheManager
(来自spring-data-redis dependency)。
如果没有命中缓存,它将继续创建缓存。下面是RediscacheManager
中的关键方法:
protected Cache getMissingCache(String name) {
return this.dynamic ? createCache(name) : null;
}
@SuppressWarnings("unchecked")
protected RedisCache createCache(String cacheName) {
long expiration = computeExpiration(cacheName);
return new RedisCache(cacheName, (usePrefix ? cachePrefix.prefix(cacheName) : null), redisOperations, expiration,
cacheNullValues);
}
本质上,它将实例化rediscache
对象。为此,它需要4个参数,即cacheName、prefix(这是回答这个问题的关键参数)、redisOperation(aka,配置的redisTemplate)、expiration(默认值为0)和cacheNullValues(默认值为false)。下面的构造函数显示了关于rediscache的更多细节。
/**
* Constructs a new {@link RedisCache} instance.
*
* @param name cache name
* @param prefix must not be {@literal null} or empty.
* @param redisOperations
* @param expiration
* @param allowNullValues
* @since 1.8
*/
public RedisCache(String name, byte[] prefix, RedisOperations<? extends Object, ? extends Object> redisOperations,
long expiration, boolean allowNullValues) {
super(allowNullValues);
Assert.hasText(name, "CacheName must not be null or empty!");
RedisSerializer<?> serializer = redisOperations.getValueSerializer() != null ? redisOperations.getValueSerializer()
: (RedisSerializer<?>) new JdkSerializationRedisSerializer();
this.cacheMetadata = new RedisCacheMetadata(name, prefix);
this.cacheMetadata.setDefaultExpiration(expiration);
this.redisOperations = redisOperations;
this.cacheValueAccessor = new CacheValueAccessor(serializer);
if (allowNullValues) {
if (redisOperations.getValueSerializer() instanceof StringRedisSerializer
|| redisOperations.getValueSerializer() instanceof GenericToStringSerializer
|| redisOperations.getValueSerializer() instanceof JacksonJsonRedisSerializer
|| redisOperations.getValueSerializer() instanceof Jackson2JsonRedisSerializer) {
throw new IllegalArgumentException(String.format(
"Redis does not allow keys with null value ¯\\_(ツ)_/¯. "
+ "The chosen %s does not support generic type handling and therefore cannot be used with allowNullValues enabled. "
+ "Please use a different RedisSerializer or disable null value support.",
ClassUtils.getShortName(redisOperations.getValueSerializer().getClass())));
}
}
}
那么prefix
在这个重排中有什么用呢?-->如构造函数about所示,在此语句this.cacheMetadata=new RedisCacheMetadata(name,prefix);
中使用,下面的RedisCacheMetadata
的构造函数显示了更多详细信息:
/**
* @param cacheName must not be {@literal null} or empty.
* @param keyPrefix can be {@literal null}.
*/
public RedisCacheMetadata(String cacheName, byte[] keyPrefix) {
Assert.hasText(cacheName, "CacheName must not be null or empty!");
this.cacheName = cacheName;
this.keyPrefix = keyPrefix;
StringRedisSerializer stringSerializer = new StringRedisSerializer();
// name of the set holding the keys
this.setOfKnownKeys = usesKeyPrefix() ? new byte[] {} : stringSerializer.serialize(cacheName + "~keys");
this.cacheLockName = stringSerializer.serialize(cacheName + "~lock");
}
至此,我们知道一些前缀参数已经设置为rediscacheMetadata
,但是这个前缀到底是如何在Redis中用来形成密钥的(例如您提到的\xac\xed\x00\x05t\x00\tvc:501381)?
基本上,CacheInterceptor
随后将向前移动,从上述Rediscache
对象调用方法私有RedisCacheKey getRedisCacheKey(对象键)
,该对象通过使用来自RediscacheMetadata
的前缀和来自Redisoperation
的keySerializer返回RedisCacheKey
的实例。
private RedisCacheKey getRedisCacheKey(Object key) {
return new RedisCacheKey(key).usePrefix(this.cacheMetadata.getKeyPrefix())
.withKeySerializer(redisOperations.getKeySerializer());
}
达到这一点,CacheInterceptor
的“pre”建议就完成了,它将继续执行@cacheable
注释的实际方法。并且在完成实际方法的执行之后,它将执行cacheinterceptor
的“post”建议,这实质上是将结果重新discache。下面是将结果放入redis缓存的方法:
public void put(final Object key, final Object value) {
put(new RedisCacheElement(getRedisCacheKey(key), toStoreValue(value))
.expireAfter(cacheMetadata.getDefaultExpiration()));
}
/**
* Add the element by adding {@link RedisCacheElement#get()} at {@link RedisCacheElement#getKeyBytes()}. If the cache
* previously contained a mapping for this {@link RedisCacheElement#getKeyBytes()}, the old value is replaced by
* {@link RedisCacheElement#get()}.
*
* @param element must not be {@literal null}.
* @since 1.5
*/
public void put(RedisCacheElement element) {
Assert.notNull(element, "Element must not be null!");
redisOperations
.execute(new RedisCachePutCallback(new BinaryRedisCacheElement(element, cacheValueAccessor), cacheMetadata));
}
在RediscacheputCallback
对象内,其回调方法DoInRedis()
实际上调用了一个方法来形成redis中的实际键,方法名为RediscacheKey
实例中的GetKeyBytes()
。下面展示了这种方法的详细内容:
/**
* Get the {@link Byte} representation of the given key element using prefix if available.
*/
public byte[] getKeyBytes() {
byte[] rawKey = serializeKeyElement();
if (!hasPrefix()) {
return rawKey;
}
byte[] prefixedKey = Arrays.copyOf(prefix, prefix.length + rawKey.length);
System.arraycopy(rawKey, 0, prefixedKey, prefix.length, rawKey.length);
return prefixedKey;
}
正如我们在getKeyBytes
方法中看到的,它同时使用了原始键(在您的示例中为vc:501381)和前缀键(在您的示例中为\xac\xed\x00\x05t\x00\t)。
好的,搜索了一会儿,在http://java.dzone.com/articles/spring-data-redis上找到了帮助。
它的发生是因为Java连载。
redisTemplate的密钥序列化程序需要配置为StringRedisserializer
,即如下所示:
<bean
id="jedisConnectionFactory"
class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
p:host-name="${redis.server}"
p:port="${redis.port}"
p:use-pool="true"/>
<bean
id="stringRedisSerializer"
class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
<bean
id="redisTemplate"
class="org.springframework.data.redis.core.RedisTemplate"
p:connection-factory-ref="jedisConnectionFactory"
p:keySerializer-ref="stringRedisSerializer"
p:hashKeySerializer-ref="stringRedisSerializer"
/>
现在redis中的关键字是vc:501381
。
或者像@niconic所说的那样,我们也可以将默认序列化器本身设置为字符串序列化器,如下所示:
<bean
id="redisTemplate"
class="org.springframework.data.redis.core.RedisTemplate"
p:connection-factory-ref="jedisConnectionFactory"
p:defaultSerializer-ref="stringRedisSerializer"
/>
这意味着我们所有的键和值都是字符串。但是请注意,这可能并不可取,因为您可能希望您的值不仅仅是字符串。
如果您的值是一个域对象,那么您可以使用jackson序列化器并配置一个序列化器,如下所述:
<bean id="userJsonRedisSerializer" class="org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer">
<constructor-arg type="java.lang.Class" value="com.mycompany.redis.domain.User"/>
</bean>
并将模板配置为:
<bean
id="redisTemplate"
class="org.springframework.data.redis.core.RedisTemplate"
p:connection-factory-ref="jedisConnectionFactory"
p:keySerializer-ref="stringRedisSerializer"
p:hashKeySerializer-ref="stringRedisSerializer"
p:valueSerialier-ref="userJsonRedisSerializer"
/>
问题内容: 我正在使用Jedis的Spring Data Redis。我试图用key存储哈希。我能够成功插入Redis。但是,当我使用redis- cli检查密钥时,看不到密钥。相反,我看到了。 为什么会发生这种情况,我该如何更改? 问题答案: 好的,谷歌搜索了一段时间,并在http://java.dzone.com/articles/spring-data- redis 找到了帮助。 它的发生是
在我的REST controllers Spring项目中,我想在Redis中存储会话信息。 在我的Application.Properties中,我定义了以下内容: 我还启用了Http Redis会话: 我终于有了一个这样的redis连接工厂: \xac\xx\x00\x05sr\x00\x0ejava.lang.long;\x8b\xe4\x90\xcc\x8f#\xdf\x02\x00\x0
我有一个使用线程(实际上是Spark)和Redis(绝地)的Scala程序。我为我的Redis操作定义了一个,其中我为连接定义了一个。我需要每个线程打开一个到Redis的连接,并与之并行工作 连接对象: 当我用一根线的时候,效果很好。但当多个线程使用它时,我会出错。起初,我在每个线程中得到了,其中“4”是一个随机字符() 然后从我尝试设置和,因为我也看到了,有时是redis。客户。绝地武士。例外。
题目描述 Java 数组扩容问题:实现动态的给数组添加元素效果,实现对数组扩容 原始数组 int[] arr = {1,2,3} 增加的元素 4,直接放在数组的最后 arr = {1,2,3,4} 题目来源及自己的思路 定义 arr1 定义 arr2,比 arr1 的长度长 1 在 arr1 的长度内,把 arr1 的值赋值给 arr2 arr2 的最后一个位置赋值为 4,也就是要加入的数据 因为
我的表单已提交,但未存储在表中。如果我对请求执行dd(),则数据在中,但当我执行save()时,它不会按预期工作。我想在backoffice表单上添加用户,仅包含姓名、电子邮件、用户类型和密码。 编辑:我将问题图像更改为代码,以便您更容易理解,很抱歉第一次尝试。编辑2:现在出现了更多的两件事,密码验证确认总是错误的,如果我跳过验证,则会出现以下错误: 对未定义方法App\User::forget(
问题内容: 我有一个MySQL表,该表由大约一百万个纬度和经度组成,每行都有一个主键值。 我想通过Geohashing或lat和lon排序集将此表迁移到Redis。 有人这样做吗?您用于存储和查询数据的方法是什么(例如,在Google Maps的纬度/经度范围内查询数据)。 问题答案: 是的,它已经完成了(使用geohashing …) 例如,您可以检查Geodis程序包后面的数据结构(来自Dvi