1. ホーム
  2. データベース
  3. レディス

Redisのインクリメント呼び出しが失敗する理由と推奨される使い方を大きな白い嘘で解説

2022-01-15 11:47:32

プロジェクトでは基本的にredisに出会うことになりますが、spring-data-redis-2.*. *RELEASE.jarには、redisに保存されたデータを簡単に操作するためのHelperクラスが2つ用意されています。この2つのHelperクラスはRedisTemplateとStringRedisTemplateで、StringRedisTemplateはString型を保存するためのRedisTemplateの拡張サブクラスです。そのため、redisを利用する際には

1.String型を操作する場合は、StringRedisTemplateを優先してください。

2、型が複雑なオブジェクトの場合、RedisTemplateの考慮は限定的です。

       コール数の記録、インターフェースのコールスレッショルドの記録など、カウントの場面でredisを使っている人が、RedisTemplateで以下のエラーERR value is not an integer or out of rangeが発生した場合、まず解決策は以下のようになります。

//Inject StringRedisTemplate directly into the class 
@Autowired
private StringRedisTemplate stringRedisTemplate;

//count in the method body with
stringRedisTemplate.opsForValue().increment("check:incr:str");

つまり、RedisTemplateをStringRedisTemplateに置き換えてください。

        上記の理由は、シリアライゼーションによるものです。redisではデータはバイナリ形式で保存されることが分かっているので、必然的にシリアライズと逆シリアライズを伴います。上記の2つのHelperクラスは、シリアライズと逆シリアライズを自動的に支援してくれますが、両者の主な違いは、シリアライズ機構にあります

1. StringRedisTemplateのシリアライズ機構は、StringRedisSerializerによって実装されています。

2. RedisTemplateのシリアライズ機構は、JdkSerializationRedisSerializerを通して実装されています。

        インクリメント操作は、一番下がデータの読み込み、次に+1、そしてsetですが、この3ステップはredisプラス原子操作で保証されているので、StringRedisTemplateとRedisTemplateからsetメソッドを解析してみましょう。まず、StringRedisTemplateのソースコードを見てみると、以下のようになっています。

1. stringRedisTemplate.opsForValue().set("check:incr:str", "1")を実行。

2. 1.のsetメソッドを確認し、DefaultValueOperationsクラスのsetメソッドに移動する

@Override
public void set(K key, V value) {
  byte[] rawValue = rawValue(value);
  execute(new ValueDeserializingRedisCallback(key) {
    @Override
    protected byte[] inRedis(byte[] rawKey, RedisConnection connection) {
      connection.set(rawKey, rawValue);
      return null;
    }
  }, true);
}

3. 値を処理するrawValueメソッドの2段階目を確認し、AbstractOperations.rawValueメソッドに移動します。

@SuppressWarnings("unchecked")
byte[] rawValue(Object value) {
    if (valueSerializer() == null && value instanceof byte[]) {
        return (byte[]) value;
    }
    return valueSerializer().serialize(value);
}


4. 3番目のステップでは、最終的にシリアライズクラスを呼び出して値のシリアライズを行っていることから、StringRedisTemplateのシリアライズクラスStringRedisSerializerが
ステップ3のserializeの最終行は、結局、以下のメソッドを呼び出していることがわかります。

@Override
public byte[] serialize(@Nullable String string) {
    return (string == null ? null : string.getBytes(charset));
}


5. この時点で、value redisの最後の存在の底原理は、4番目のステップでreturnで返されたbyte[]であることがわかります。では、こう考えてみてください。
stringRedisTemplate.opsForValue().set("check:incr:str", "1"); あたかも "1".getBytes("UTF-8& quot;) のようなものである。

public static void main(String[] args) throws UnsupportedEncodingException {
  byte[] str = "1".getBytes("UTF-8");
  for(int i = 0 ; i< str.length; i++) {
    System.out.println(str[i]);
  }
}


結論

上のmainメソッドは49を出力していますが、setメソッドを理解した上で、incrementの呼び出しは49に直接1を足して50にし、データを取得する際に逆シリアライズして数字の2に対応することを示すのと同じだと推測することができます

[...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...]   [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...] [...]