このエラーが発生する理由スタンドアロンのRedisインスタンスからクラスターへの移行は、通常はスムーズに進みます。しかし、Luaスクリプトを実行しようとした瞬間に状況が変わることがあります。突然、EVALコマンドがクラッシュし、ERR Script attempted to access a non-local key in a cluster nodeという厄介なエラーが表示されます。これは、クラスターが複数のノードにデータが分散されている分散システムであるために発生します。単一のインスタンスとは異なり、クラスターノードはデータ全体の特定の「スライス」のみを保持しています。
根本原因:データシャーディングとハッシュスロットRedis Clusterはキースペースを正確に16,384個のハッシュスロットに分割します。各ノードはこれらのスロットの特定の範囲を担当します。例えば、ノードAがスロット0から5,460を担当し、ノードBが5,461から10,922を担当するといった具合です。Luaスクリプトを実行する場合、Redisはスクリプトが操作するすべてのキーが同じハッシュスロットに存在することを要求します。
このエラーは、以下のようなシナリオで発生します:
- スクリプトがノード1から
key_aを、ノード2からkey_bを読み取ろうとした場合。- コマンドを受け取ったノードに属さないキー名をスクリプト内にハードコードした場合。- 異なるスロットにハッシュされるキーを引数として渡した場合。Redisは原子性を維持するために、このルールを厳格に適用しています。もしスクリプトが異なるノード間のキーを操作できてしまうと、Redisは非常にコストの高いノード間ロックを実装する必要があり、パフォーマンスが著しく低下してしまいます。
エラーの解決方法### 解決策1:キーのグルーピングにハッシュタグを使用する最も信頼できる解決策は、ハッシュタグを使用することです。キー名の一部を中括弧{}で囲むことで、Redisにその括弧内のテキストのみをハッシュするように指示できます。これにより、異なるキーを強制的に同じノード上の同じスロットに配置できます。
例: 以下のキーは、3つの異なるノードに分散される可能性があります:
user:123:profile
user:123:settings
user:123:stats
ハッシュタグを使用して、これらをグループ化します:
{user:123}:profile
{user:123}:settings
{user:123}:stats
これで、Redisは文字列user:123のみをハッシュします。これにより、3つのキーすべてが確実に同じスロットに配置され、単一のLuaスクリプトからアクセス可能になります。
解決策2:常にKEYS配列経由でキーを渡すRedis Clusterは、スクリプトが実際に実行される前に、どのキーを操作するかを知る必要があります。これにより、クライアントはリクエストを正しいノードにルーティングできます。Luaコード内にキーをハードコードすると、クラスターは事前にその場所を検証できません。
誤った方法(ハードコード):
-- クラスター環境では失敗する可能性が高い
local val = redis.call('GET', 'global_settings')
return val
正しい方法(引数として渡す):
-- アプリケーションコードから KEYS[1] として 'global_settings' を渡す
local val = redis.call('GET', KEYS[1])
return val
KEYS配列を使用すると、ioredis、lettuce、redis-pyなどのクラスター対応クライアントが最初にスロットを計算します。その後、そのキーを保持している特定のノードにスクリプトを送信します。
解決策3:ロジックをアプリケーション層に移動するどうしてもキーを同じ場所に配置できない場合があります。異なるスロットに存在する2つのユーザーのデータを比較する必要がある場合、単一のLuaスクリプトは機能しません。この場合は、アプリケーションコード側でロジックを処理する必要があります:
- Redisから
user:123:dataを取得する。- Redisからuser:456:dataを取得する。- Node.js、Go、Pythonなどで比較やロジックを実行する。- 必要に応じて結果をRedisに書き戻す。## 検証手順redis-cliのcluster keyslotコマンドを使用して、キーが同じスロットを共有しているか確認します:
# profileキーのスロットを確認
redis-cli -c CLUSTER KEYSLOT {user:123}:profile
# 出力: 1234
# settingsキーのスロットを確認
redis-cli -c CLUSTER KEYSLOT {user:123}:settings
# 出力: 1234
出力された数値が一致すれば、スクリプトはエラーなしで実行されます。

