エラーの発生シナリオトラフィックスパイクに対応するために、4つ目のノードを追加したり、16,384個のスロットをリバランスしたりしてRedis Clusterをスケーリングしているとします。すると突然、アプリケーションログに特定のエラーが急増します。(error) TRYAGAIN Multiple keys request during rehashing of slot というエラーです。これにより、通常、MGET、MSET、またはカスタムLuaスクリプトなどの複数のキーを含むコマンドの操作が停止します。
このエラーは、コマンドが同じハッシュスロットに存在する複数のキーを対象としているものの、そのスロットが現在ノード間で移行中である場合に発生します。データが移行元と移行先のノードに分散している間、Redisは原子性を保証することができません。
発生の理由Redis Clusterはデータを16,384個のハッシュスロットに分割します。スロットをノードAからノードBに移行すると、スロットは移行元で「migrating(移行中)」状態、移行先で「importing(インポート中)」状態になります。この遷移期間中、キーはバッチ単位で移動されます。
単一キーのコマンドの処理は簡単です。キーがすでに存在しない場合、ノードAは ASK リダイレクションを送信し、クライアントはノードBで再試行します。しかし、マルチキーコマンドは異なります。もし MGET が10個のキーをリクエストし、そのうち7個がノードBに移動済みで3個がノードAに残っている場合、Redisはいずれのノードでもリクエストを処理できません。不完全なデータや不整合なデータを返す代わりに、Redisは TRYAGAIN エラーをスローします。これは本質的に、その特定のバッチの移行が完了するまで待機するようクライアントに求めているのです。
ステップ1:移行中のスロットを特定するこの問題を解決するには、まず遷移途中で止まっているスロットを特定する必要があります。いずれかのクラスターノードで次のコマンドを実行し、現在どのスロットが移動中かを確認します:
redis-cli -p 6379 CLUSTER NODES | grep -E "importing|migrating"
出力は以下のようになります:
node_id_1 192.168.1.10:6379@16379 master - 0 1625000000000 2 connected 0-5460 [5461->-node_id_2]
node_id_2 192.168.1.11:6379@16379 master - 0 1625000000000 3 connected 5462-10922 [5461-<-node_id_1]
この特定のケースでは、スロット 5461 が移行の途中にあります。スロット5461内のキーにアクセスするマルチキーコマンドは、移動が完了するまで失敗します。
ステップ2:アプリケーション側にリトライロジックを実装するTRYAGAIN は一時的なエラーであるため、通常は数ミリ秒で解決します。恒久的なトポロジの変更を意味する MOVED エラーとは異なり、TRYAGAIN は一時的な停止を必要とするだけです。Python(redis-py)を使用している場合は、マルチキーの呼び出しをリトライループでラップしてください。
import time
import redis
def execute_with_retry(client, keys, max_retries=5):
attempt = 0
while attempt < max_retries:
try:
return client.mget(keys)
except redis.exceptions.ResponseError as e:
if "TRYAGAIN" in str(e):
# 再試行の前に25ミリ秒待機
time.sleep(0.025)
attempt += 1
continue
raise e
raise Exception(f"Command failed after {max_retries} retries due to slot migration.")
ステップ3:インフラストラクチャのボトルネックを解消するエラーが数秒以上続く場合は、移行が停滞している可能性があります。これは多くの場合、単一のスロットに「ホットキー」や、シリアル化と送信に時間がかかる巨大なO(N)データ構造が含まれている場合に発生します。
移行速度を上げるパイプラインのサイズを大きくすることで、プロセスを高速化します。デフォルトでは、redis-cli はCPUスパイクを避けるためにキーを低速で移動します。これを1回のラウンドトリップにつき1個ではなく100個のキーを移動するように設定することで、高速化できます:
redis-cli --cluster reshard <ip>:<port> --cluster-pipeline 100
スロットを強制的に安定(stable)させる移行が完全にハングアップしている場合は、手動でスロットの状態をstableに設定する必要があるかもしれません。これを行うのは、データが移行先に存在することを確認した場合、または直後にfixコマンドを実行する予定がある場合のみにしてください。移行元と移行先の両方のノードで以下を実行します:
redis-cli -h <node_ip> -p 6379 CLUSTER SETSLOT 5461 NODE <destination_node_id>
検証チェックツールを実行して、クラスターが正常な状態に戻ったことを確認します:
redis-cli --cluster check 192.168.1.10:6379
[OK] All 16384 slots covered. という確認メッセージが表示されるか確認してください。「Nodes don't agree about configuration」と表示された場合は、redis-cli --cluster fix を実行してスロットマップを同期させます。

