Spring-data-redisでset()、get()、increment()を同時に使用する問題
8571 ワード
Spring-data-redisでset()、get()、increment()を同時に使用する問題
1.問題の説明
最近コードを開発して、redisを使う時、1つの面白い問題に出会って、問題のコードは以下の通りです:
コード注記の説明:問題1:まずset key 1、更にget key 1は問題ありませんが、incrementは間違いを報告し、間違いを報告する内容は以下の通りです: 問題2:incrementkey 2(key 2以前は存在しなかった)を先にincrement key 2してからget key 2がエラーを報告し、エラー内容は以下の通り:
2.問題分析
上記のエラーを分析すると、両方の問題はredisに格納されているデータフォーマットに問題があることがわかりますが、なぜこのような問題があるのでしょうか.
Springドキュメント(Spring Data Redis 10.8. Serializers)を参照すると、SpringがRedisに一般的にシーケンス化するポリシーには2つあることがわかります.
Multiple implementations are available (including two that have been already mentioned in this documentation): the
ここで、
RedisTemplateは、JdkSerializationRedisSerializerを用いてシーケンス化されており、シーケンス化された値にはオブジェクト情報、バージョン番号、クラス情報などが含まれており、一連の文字列であるため、数値自増操作はできない.
一方、StringRedisTemplateシーケンス化ポリシーは、文字列の値が直接バイト配列に変換されるため、redisに格納されるのは数値であるため、自己増加操作が可能である.
したがって,コードに注入されるのは
3.解決策
解決方法は簡単で、
注記:StringRedisTemplateのインスタンスvalueはStringのみ
またはredisTemplateを使用する前にシーケンス化ポリシーを設定する
注:valueのシーケンス化ツールの違いを設定します. new StringRedisSerializer()で、valueはString(コード1など) のみです. new GenericToStringSerializer<>(Integer.class)は、パラメータによって異なるタイプのvalueを設定できますが、最後にredisに保存するとStringに移行し、取り出すと最後に設定のタイプに移行し、便利(コード2など) 注意1と2の取得値はいずれもタイプ変換が必要である.これは、汎用を設定しない場合、デフォルトのパラメータタイプがObjectであるため、コードが明確であるためにkeyとvalueのタイプを設定することを提案し、ideaもコード3のようにハイライトしない である.
コード1:
コード2:
コード3:
1.問題の説明
最近コードを開発して、redisを使う時、1つの面白い問題に出会って、問題のコードは以下の通りです:
class Test {
/**
* redis
*/
@Autowired
private RedisTemplate redisTemplate;
public void test() {
// set key1, get key1 , increment
redisTemplate.opsForValue().set("key1", 1);
Integer val1 = redisTemplate.opsForValue().get("key1");
Long incr1 = redisTemplate.opsForValue().increment("key1");
// increment key2(key2 ), get key2
Long incr2 = redisTemplate.opsForValue().increment("key2");
Integer val2 = redisTemplate.opsForValue().get("key2");
}
}
コード注記の説明:
org.springframework.dao.InvalidDataAccessApiUsageException: ERR value is not an integer or out of range; nested exception is redis.clients.jedis.exceptions.JedisDataException: ERR value is not an integer or out of range
at org.springframework.data.redis.connection.jedis.JedisExceptionConverter.convert(JedisExceptionConverter.java:69) ~[spring-data-redis-2.3.6.RELEASE.jar:2.3.6.RELEASE]
at org.springframework.data.redis.connection.jedis.JedisExceptionConverter.convert(JedisExceptionConverter.java:42) ~[spring-data-redis-2.3.6.RELEASE.jar:2.3.6.RELEASE]
at org.springframework.data.redis.PassThroughExceptionTranslationStrategy.translate(PassThroughExceptionTranslationStrategy.java:44) ~[spring-data-redis-2.3.6.RELEASE.jar:2.3.6.RELEASE]
at org.springframework.data.redis.FallbackExceptionTranslationStrategy.translate(FallbackExceptionTranslationStrategy.java:42) ~[spring-data-redis-2.3.6.RELEASE.jar:2.3.6.RELEASE]
org.springframework.data.redis.serializer.SerializationException: ; nested exception is com.caucho.hessian.io.HessianProtocolException: unknown code for readObject at 0x31 (1)
at tech.joymo.framework.redis.sserializer.HessianSerializer.deserialize(HessianSerializer.java:61) ~[joymo-framework-redis-1.0-20210202.122946-5.jar:1.0-SNAPSHOT]
at org.springframework.data.redis.core.AbstractOperations.deserializeValue(AbstractOperations.java:335) ~[spring-data-redis-2.3.6.RELEASE.jar:2.3.6.RELEASE]
at org.springframework.data.redis.core.AbstractOperations$ValueDeserializingRedisCallback.doInRedis(AbstractOperations.java:61) ~[spring-data-redis-2.3.6.RELEASE.jar:2.3.6.RELEASE]
at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:228) ~[spring-data-redis-2.3.6.RELEASE.jar:2.3.6.RELEASE]
at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:188) ~[spring-data-redis-2.3.6.RELEASE.jar:2.3.6.RELEASE]
at org.springframework.data.redis.core.AbstractOperations.execute(AbstractOperations.java:96) ~[spring-data-redis-2.3.6.RELEASE.jar:2.3.6.RELEASE]
at org.springframework.data.redis.core.DefaultValueOperations.get(DefaultValueOperations.java:53) ~[spring-data-redis-2.3.6.RELEASE.jar:2.3.6.RELEASE]
2.問題分析
上記のエラーを分析すると、両方の問題はredisに格納されているデータフォーマットに問題があることがわかりますが、なぜこのような問題があるのでしょうか.
Springドキュメント(Spring Data Redis 10.8. Serializers)を参照すると、SpringがRedisに一般的にシーケンス化するポリシーには2つあることがわかります.
Multiple implementations are available (including two that have been already mentioned in this documentation):
JdkSerializationRedisSerializer
, which is used by default for RedisCache
and RedisTemplate
. StringRedisSerializer
. ここで、
RedisTemplate
のデフォルトのシーケンス化ポリシーはJdkSerializationRedisSerializer
であり、StringRedisTemplate
のシーケンス化ポリシーはStringRedisSerializer
である.RedisTemplateは、JdkSerializationRedisSerializerを用いてシーケンス化されており、シーケンス化された値にはオブジェクト情報、バージョン番号、クラス情報などが含まれており、一連の文字列であるため、数値自増操作はできない.
一方、StringRedisTemplateシーケンス化ポリシーは、文字列の値が直接バイト配列に変換されるため、redisに格納されるのは数値であるため、自己増加操作が可能である.
したがって,コードに注入されるのは
RedisTemplate
実装であり,JdkSerializationRedisSerializer
をシーケンス化方法として用いているため,setとgetの両方でシーケンス化と逆シーケンス化が行われ,increment操作ではシーケンス化が行われないため,上記の2つの問題が生じる.3.解決策
解決方法は簡単で、
JdkSerializationRedisSerializer
のシーケンス化による問題は、シーケンス化をStringRedisSerializer
に置き換えればよい.コードは以下の通りであり、RedisTemplate
とStringRedisTemplate
の両方を直接注入してStringRedisTemplateの例を得ることができ、それによって問題を解決することができる.注記:StringRedisTemplateのインスタンスvalueはStringのみ
class Test {
/**
* redis
*/
@Autowired
private RedisTemplate redisTemplate;
/**
* redis
*/
@Autowired
private StringRedisTemplate stringRedisTemplate;
public void test() {
// set key1, get key1 , increment
redisTemplate.opsForValue().set("key1", "1");
String val1 = redisTemplate.opsForValue().get("key1");
String incr1 = redisTemplate.opsForValue().increment("key1");
// increment key2(key2 ), get key2
String incr2 = redisTemplate.opsForValue().increment("key2");
String val2 = redisTemplate.opsForValue().get("key2");
}
}
またはredisTemplateを使用する前にシーケンス化ポリシーを設定する
注:valueのシーケンス化ツールの違いを設定します.
コード1:
class Test {
/**
* redis
*/
@Autowired
private RedisTemplate redisTemplate;
public void test() {
redisTemplate.setValueSerializer(new StringRedisSerializer());
// set key1, get key1 , increment
redisTemplate.opsForValue().set("key1", "1");
String val1 = (String) redisTemplate.opsForValue().get("key1");
Long incr1 = redisTemplate.opsForValue().increment("key1");
// increment key2(key2 ), get key2
Long incr2 = redisTemplate.opsForValue().increment("key2");
String val2 = (String) redisTemplate.opsForValue().get("key2");
}
}
コード2:
class Test {
/**
* redis
*/
@Autowired
private RedisTemplate redisTemplate;
public void test() {
redisTemplate.setValueSerializer(new GenericToStringSerializer<>(Integer.class));
// set key1, get key1 , increment
redisTemplate.opsForValue().set("key1", 1);
Integer val1 = (Integer) redisTemplate.opsForValue().get("key1");
Long incr1 = redisTemplate.opsForValue().increment("key1");
// increment key2(key2 ), get key2
Long incr2 = redisTemplate.opsForValue().increment("key2");
Integer val2 = (Integer) redisTemplate.opsForValue().get("key2");
}
}
コード3:
class Test {
/**
* redis
*/
@Autowired
private RedisTemplate redisTemplate;
public void test() {
redisTemplate.setValueSerializer(new StringRedisSerializer());
// set key1, get key1 , increment
redisTemplate.opsForValue().set("key1", 1);
Integer val1 = redisTemplate.opsForValue().get("key1");
Long incr1 = redisTemplate.opsForValue().increment("key1");
// increment key2(key2 ), get key2
Long incr2 = redisTemplate.opsForValue().increment("key2");
Integer val2 = redisTemplate.opsForValue().get("key2");
}
}