Redis Lua スクリプトは、条件付きを含む複数の Redis コマンドをサーバー上で隔離された状態で実行します。これにより、読み取り、検証、書き込みの間に、他のクライアントによる矛盾した中間状態が生じることはありません。. ここでいう「アトミック」とは、自動ロールバックを意味するものではありません: 入力とエラー処理のフローは、とりわけ書き込み操作の前に、慎重に設計する必要があります。明確に宣言されたキー、安定した戻り値、短い実行時間、そしてネイティブコマンドからRedis関数に至るまで、適切なモデルの選択が鍵となります。.
RedisのLuaスクリプトを分類する
Redis Lua スクリプトは、ビジネスロジックを Redis サーバー内で直接実行します。スクリプトの実行中は、Redis は他のサーバー処理を行わないため、スクリプトに含まれるコマンドは他のクライアントから隔離されます。これにより、複数の単純なコマンドを 1 つの 原子操作 組み合わせることも可能です。例えば、限度額チェックの後に残高を更新する処理や、残高が十分にある場合にのみ引き落としを行うといった処理などです。.
スクリプトがない場合、クライアントはまずGETでカウンタを読み取り、アプリケーションコード内で制限値を確認した上で、INCRを送信することになります。しかし、これらのステップの間に、別のクライアントが同じカウンタを変更してしまう可能性があります。 一方、スクリプトは、こうした目に見える中間状態を経ることなく、読み取り、検証、インクリメントを行います。これにより、複合ルールのレースコンディションは解消されますが、適切な閾値、処理時間、返却形式といった問題については自動的に解決されるわけではありません。.
「孤立した」や「孤立した」という意味は、決して Redis Lua スクリプト データベーストランザクションは自動ロールバックが行われる。書き込み操作が完了した後に実行時エラーが発生した場合でも、それ以前の変更が一括して取り消されるわけではない。そのため、スクリプトでは最初の書き込みを行う前に、入力内容、データ型、および業務上の要件を確認すべきである。変更後のエラー処理については、意図的に設計された処理が必要となる。.
典型的なルールとしては、制限範囲内でのみアクセスを許可する、あるいは十分な数量がある場合にのみ在庫を減らす、といったものがあります。まず、既存の単一のRedisコマンドでそのルール全体を表現できるかどうかを確認してください。スクリプトが有用なのは、複数のRedis操作とその条件が、アトミックに連携して動作する必要がある場合です。.
「比較・削除」のためのLuaパターンでは、保存された値を渡された所有権トークンと照合し、一致した場合にのみ削除を行います。これにより、処理が遅れた場合でも、古いトークンを持っているという理由だけで、その間に再割り当てされたキーを削除してしまうことを防ぐことができます。.
この比較例は、単一の Redis キーに対する安全な順序についてのみ説明しています。分散ロックに関する、適切なリース期間、プロセスの一時停止、障害、あるいは複数の Redis インスタンス間の連携といった、より広範な問題については扱っていません。 また、コマンドやスクリプトの原子性は、関連するRedisデータのみを対象としており、支払い、データベース、電子メール、または外部APIは対象外です。.
Luaサンドボックスと明確な境界
Redis Open Source は、スクリプト用に Lua 5.1 を組み込んでいます。このランタイムは、ローカルにインストールされた Lua や最新の Lua メジャーバージョンと同等とはみなせません。利用可能な言語機能やセキュリティルールは Redis によって決定されます。 したがって、RedisのLuaスクリプトを開発する際は、実際に使用されているRedisのバージョンに対して動作確認を行うべきであり、任意の外部Lua環境の特性を前提にしてはなりません。.
実行は、1つの サンドボックス 意図的に制限が設けられています。スクリプトはRedisのデータと渡された引数を処理するものではありますが、ファイルシステム、ネットワーク、OSサービスを一切利用してはなりません。したがって、外部へのHTTP呼び出し、メッセージの送信、ローカルファイルへのアクセスなどは、アプリケーションコードまたはそのための専用サービスで行うべきであり、キャッシュスクリプティングで行うべきではありません。.
Redisは KEYS そして ARGV グローバルな実行時変数として利用できます。一方、独自の中間値や補助関数には、 local. これにより、どの値がこの呼び出しにのみ適用されるかが明確になり、スクリプトのロジックが不必要な依存関係を生み出すことを防げます。Redisコマンドは、 redis.call 或いは redis.pcall にある。
サンドボックスはキャパシティプランニングの代わりにはなりません。通常の実行時、スクリプトはサーバー上で他のクライアントの処理を妨げるため、長いループ、無制限のデータ量、および計算負荷の高い処理には適していません。処理は、あらかじめ把握している少数のキーと、小規模な計算に限定してください。 大規模な分析、SCAN ベースの総在庫管理、あるいは外部システムとの通信は、原子性を有意義に拡張することなく、運用リスクを高めることになります。.
EVAL、KEYS、ARGV の理解
スクリプトを直接呼び出す場合は、次の形式を使用します。 EVAL script numkeys [key …] [arg …]. ソースコードによると、numkeys は、続くパラメータのうち何個がキーであるかを指定します。 スクリプトは、1を基点とするインデックス付けでKEYSを介してこれらにアクセスします。その他の値はすべてARGVに含まれます。この区別は極めて重要です。キーはRedisデータを記述し、引数は閾値、絶対値、または期待されるトークンといった業務上の入力を記述します。.
たとえば、リミットスクリプトは、カウンタを KEYS[1] として、上限値を ARGV[1] として受け取ります。このスクリプトは現在の値を読み取り、 tonumber(ARGV[1]) 数値に変換し、値を増やす前に両者を比較します。この変換により、引数の値を暗黙的に扱うのではなく、意図した数値演算規則が明示的に示されます。カウンタがない場合、スクリプトは読み取った値を意図的にゼロとして扱うことができます。.
スクリプトが読み書きするすべてのキーは、事前にキー引数として指定する必要があります。スクリプト内で接頭辞を組み合わせてキー名を構成したり、保存されたデータからキー名を導出したりすることは、堅牢な設計パターンとは言えません。 特に、Redis Open Sourceでクラスタ機能が有効になっている場合、Redisはスクリプトの実行前に、そのスクリプトがどのデータを必要とするかを把握できません。したがって、既知のキーはKEYSを介して完全に渡し、可変のフィールド値はARGVを介してのみ渡すようにしてください。.
Redis Open Source でクラスタ機能が有効になっている場合、スクリプトに渡されるキーは、同じハッシュスロットにある必要があります。前述の宣言によりこのチェックが可能になりますが、チェックそのものを置き換えるものではありません。関連するデータについては、意図的に選択したハッシュタグが役立つ場合があります。例えば、 account:{4711}:balance そして account:{4711}:reservations. ここでは、波括弧で囲まれた部分がスロットの割り当てを決定しており、動的に決定されたキーはこの割り当てを無効にしてしまうことになる。.
固定ウィンドウカウンタをアトミックに更新する
次の例はアトミックな 固定ウィンドウカウンタ ローカルのテストインスタンス用です。これは1回のサーバー実行でカウンタの値と閾値を確認し、時間枠内で初めて正常にアクセスできた場合にのみ有効期限を設定します。これにより、アプリケーションコード内のGET呼び出しと、その後のINCR呼び出しの間に生じる時間枠において、他のクライアントによってカウンタが変更される可能性がなくなります。.
この呼び出しでは、カウンタキー、制限値、およびウィンドウ期間(秒単位)が渡されます。 ステータス 1 は許可を意味し、ステータス 0 は制限に達したことを意味します。ステータス 2 は、事前チェックで無効な入力が検出された場合、そこで拒否された文字列カウンター値、または TTL の設定がない既存の文字列カウンターがあることを示します。 キーに他のRedisデータ型が含まれている場合、GETは技術的な型エラーで失敗し、スクリプトはステータス2を返しません。また、その他のRedisランタイムエラーも、業務上の戻りステータスとは区別する必要があります。この例は、アクセスデータ、本番環境での制限、または負荷テストのテンプレートではありません。.
スクリプトは、書き込み操作を行うたびに、すべての数値が、意図的に低く設定された上限値以内の有限の正の整数であることを検証します。これは、単なる tonumber: 以下のような価値観 1.5 或いは 1e3 は却下されます。また、100万という上限により、Luaの数値精度や INCR 例示の範囲外で、期待される整数文字列が関連してくる。最大ウィンドウ期間である86,400秒は、 EXPIRE 渡された秒数。.
この正規表現は10進数の数字のみを受け付け、その後、補助関数が数値、整数性、および上限値をチェックします。 既存のカウンタは、同じ制限範囲内の非負の整数値でなければなりません。これにより、負の値、小数、または上限を超える値によって、制限のセマンティクスが気付かれずに変更されることを防ぎます。これらのチェックが完了して初めて、 INCR.
そのキーが存在しない場合、スクリプトは0から開始します。有効期限のない有効な文字列カウンタがすでに存在する場合、ステータス2を返し、何も書き込みません。最初の INCR セット EXPIRE 事前に完全に検証済みのTTL。その後のヒットがあっても、これは変更されないため、ウィンドウは継続的に延長されることはありません。.
仝 返還契約 これはインターフェースの一部です。配列の最初の要素はステータスを表し、2番目の要素はステータスに応じてカウンタ値またはエラー識別子を返します。 呼び出し側のコードでは、ステータス 0 の業務上の拒否と、前提条件違反を示すステータス 2 を区別して処理する必要があります。実行時間の選択と監視に関する詳細については、以下の記事を参照してください。 Redisのキーの有効期限を分析・最適化する 補足的な根拠。.
ここでは、TTLは意図的に最初のヒット時にのみ設定されています。アクセスごとに更新されるようなパターンでは、時間の意味合いが異なり、もはや「固定ウィンドウ」とはみなされなくなります。. Luaの原子性 レースコンディションを解消するだけです。固定ウィンドウ、スライディングウィンドウ、トークンバケットのどれが、求める公平性や負荷分散に適しているかは、選択したアルゴリズムによって決まるものであり、スクリプト言語によって決まるものではありません。.
適切な微粒化モデルを選択する
すべての複合要件にスクリプトが必要というわけではありません。ビジネスルールを完全に表現できる単一の Redis コマンドが存在する場合、そのコマンドの方が運用や検証が容易なことがほとんどです。一方、多段階のルールについては、条件、データ型、および戻り値の型を総合的に考慮する必要があります。.
Redis Open Source 8.4 以降では、個々の文字列キーに対してネイティブな Compare-and-Set および Compare-and-Delete 操作が利用可能になりました: SET 比較機能をサポートしています IFEQ/IFNE/IFDEQ/IFDNE; DELEX 条件付き削除を処理します。これにより、該当する単一キーのケースでは、独自の比較スクリプトが不要になります。Redis 8.2、8.0、および 7.x では、これらの新しい SET オプションと DELEX は利用できません。これらのバージョンでは、引き続き適切な WATCH または Lua パターンが有効です。.
楽観的なCompare-and-Setでは、MULTIやEXECの前にWATCHを挿入するのが適切である場合がある。監視対象のキーがEXECの前に変更された場合、トランザクションは中止され、クライアントが再試行を行うかどうかを判断する。 また、トランザクションにおいても、EXEC中にエラーが発生した場合、一般的なロールバックは行われません。したがって、必要な条件が単一のネイティブコマンドでは実現できない場合、WATCHは依然として選択肢の一つとなります。.
| モデル | 適切な用途 | コードと呼び出し | 再起動またはフェイルオーバーの後 | クライアントの挙動と制限 |
|---|---|---|---|---|
| ネイティブコマンド | 既存の単一操作がルールを反映している | プログラムコードではなく、直接のコマンド | スクリプトキャッシュには影響なし | スクリプトの再読み込みなし;既存の意味論に限定される |
| Redis Open Source 8.4 以降のネイティブ CAS/CAD | 値に基づいて個々の文字列キーを設定または削除する | IFEQ/IFNE/IFDEQ/IFDNE を含む SET;比較条件付き DELEX | スクリプトキャッシュには影響なし | バージョンの制限と比較条件を確認する;複合マルチキー規則ではない |
| WATCH 付き MULTI/EXEC | 前向きな読み方、確認の仕方、書き方 | WATCH、MULTI、EXEC | プログラムメモリなし | EXEC実行前に変更があった場合は、再度読み込んで判断する。EXECエラーが発生してもロールバックは行わない。 |
| EVAL | 直接呼び出される小さなスクリプト | 各EVALのソースコード | スクリプトキャッシュは永続的ではありません | ダイジェストの再送信は行われません。ソースコードが再度送信されます。 |
| SCRIPT LOAD および EVALSHA | 既知のダイジェストを持つ再利用されたスクリプト | 読み込み後、SHA1ダイジェストによる呼び出し | キャッシュが存在しない可能性があります | NOSCRIPT を処理して再読み込みする;パイプラインのフォールバックを特に計画する |
| Redis Functions(バージョン7.0以降) | 名前付き、再利用可能なデータロジック | FUNCTION LOAD、その後FCALL | ライブラリはレプリケートされ、永続化されます | バージョン管理および展開プロセスが必要。EVALと同一視してはならない。 |
EVALスクリプトはスクリプトキャッシュに紐付けられており、KEYSおよびARGVを通じて入力を受け取ります。. Redis 関数 Redis 7.0 以降では、名前付きライブラリとして利用可能です。これらは FUNCTION LOAD で登録され、FCALL で呼び出され、データベースと共に永続化およびレプリケーションされます。キーと引数はパラメータとして関数に渡されるため、EVAL とは異なる提供および呼び出しモデルとなります。.
したがって、アプリケーションに近い小規模なロジックについては、EVALが最も手軽な入り口となります。 複数のクライアントや長期的に維持管理されるデータロジックを扱う場合は、使用しているRedisのオープンソース版がFunctionsをサポートしている限り、Functionsの採用が適していることが多い。また、決定にあたっては、Redisコマンドの数だけでなく、デプロイ、権限、エラー処理、そして明確に文書化された戻り値についても考慮すべきである。.
クラスタ、エラー、および戻り値契約
Redis Open Sourceでクラスタ機能が有効になっている場合、マルチキースクリプトで渡されるキーは、すべて同じハッシュスロットに属している必要があります。ハッシュタグを使用することで、これを制御できます。例えば、 account:{4711}:balance そして account:{4711}:reservations 波括弧で囲まれた内容がスロットを決定します。したがって、両方のキーを同時に参照することができます。この「同一スロット」の要件は、ここで検討しているマルチキー操作やMULTI/EXECトランザクションにも適用されます。製品やクラスタの構成によっては、個々のコマンドで異なる場合があります。 したがって、Lua に対して一般的なクロススロットの許可が与えられるわけではありません。マルチキーに関するドキュメントでは、Redis Software において、クラスタが有効化されている場合、また OSS クラスタ API の有無にかかわらず、EVAL/EVALSHA はシングルスロット操作として分類されています。.
使用するすべてのキーは、呼び出し前にキー引数として宣言する必要があります。スクリプトは、保存された値からキー名を導出したり、動的に組み立てたりしてはなりません。 このルールにより、Redisは実行前にスロットのチェックを正しく行えるようになり、スタンドアロンインスタンスでは気づかれにくい隠れた依存関係が、クラスタ機能が有効になっているRedis Open Sourceではエラーとなるのを防ぎます。.
と一緒に redis.call() 呼び出されたRedisコマンドのエラーは、スクリプトエラーとしてクライアントに通知されます。. redis.pcall() その代わりに、スクリプトがそれを適切に処理できるよう、Lua に返します。pcall が有用なのは、適切に構造化されたエラー応答や、代替の許容される処理フローなど、技術的な対応が定義されている場合に限られます。エラーを黙って無視することは、データや整合性の問題を隠蔽することになります。.
A 無効な契約 技術的なエラーと業務上の結果を区別します。例えば、WRONGTYPE は、保存された Redis データ型が期待されるコマンドと一致しないことを意味し、調査が必要です。 一方、在庫不足による予約の拒否は想定された結果であり、例えばステータスや残在庫を返すことができます。アプリケーションでは、これらのカテゴリを同一に扱ったり、両方を一律に繰り返したりしてはなりません。.
スクリプトの配信を堅牢に運用する
EVAL 直接呼び出しに適しています:クライアントは、Luaのソースコード全体を、キーと引数の値とともに送信します。頻繁に使用される、変更されていないスクリプトの場合、アプリケーションは代わりに SCRIPT LOAD スクリプトキャッシュに読み込む。Redisはこの操作に対してSHA1ダイジェストを返す;; EVALSHA その後、それに対応するソースコードを正確に実行します。これにより、繰り返し転送する手間が省けますが、スクリプトの原子性や業務上の責任には影響しません。.
仝 スクリプトキャッシュ 永続的ではありません。再起動、フェイルオーバー、または SCRIPT FLUSH ダイジェストによる呼び出しは、 NOSCRIPT 失敗する。アプリケーションはこのケースを通常通り処理すべきである。つまり、独自の再試行ロジックが許容する限り、スクリプトを再読み込みし、技術的に確実な呼び出しを繰り返す。したがって、ダイジェストは、そのスクリプトがすべてのターゲットサーバーにすでに存在するという保証とは解釈してはならない。.
パイプラインの場合、このフォールバック機能には制限があります。複数のコマンドがすでにまとめて送信されている場合、アプリケーションはその中に含まれる NOSCRIPT- エラーが発生した場合、その箇所をロードして再実行することで、遡及的に置き換えないでください。Redis では、このような場合、パラメータ化された EVAL 代替戦略として。レプリケーションやフェイルオーバーを計画する際は、レプリカの再接続においてレプリケーションバッファがどのような役割を果たすかを理解しておく必要があります: Redisのレプリケーション・バックログを理解する.
変数の値は Lua のソースコードには記述せず、 ARGV. そうしないと、しきい値ごとに異なるスクリプトが生成され、キャッシュが不必要に肥大化してしまいます。Redis 7.4 以降では、 EVAL 或いは EVAL_RO キャッシュの制限に達した場合、LRU方式に従って読み込まれたスクリプトが削除されます。これは、パラメータ設定や NOSCRIPT.
長いスクリプトや誤字脱字を克服する
Luaスクリプトは、通常の実行中に他のサーバー活動をブロックします。これにより隔離が図られますが、実行時間が長くなると、 事業リスク. スクリプトが設定された busy-reply-threshold, Redisは通常のコマンドに対して次のように応答します BUSY; スクリプトは自動的に終了しません。そのため、スクリプトは、よく知られたキーを数個に限定し、計算も小規模で限定的なものにとどめてください。.
エラーや無限ループが発生する直前の書き込み操作は、特に危険です。スクリプトがすでにデータを変更してしまった場合、 SCRIPT KILL 正常に終了しない可能性があります。そのため、最初の書き込みを行う前に入力を確認し、無制限のループや SCAN 総保有数について。テストでは、計画されている運用におけるデータ量とエラー発生経路を再現する必要があります。.
| ケース | 明確な回答 | 典型的な原因 | 確かな一貫性 |
|---|---|---|---|
| NOSCRIPT | エラー応答 NOSCRIPT | 一時的なスクリプトキャッシュにダイジェストが含まれていない | スクリプトを読み込むか、パラメータ指定のEVALを使用する。独自の再試行ルールに従ってのみ、再試行を行う。. |
| CROSSSLOT | クラスタが有効になっているRedis Open SourceでのCROSSSLOT | スクリプトで渡されたキーは、さまざまなハッシュスロットに格納されています | キーの設計を変更し、必要なキーをすべて宣言する。. |
| WRONGTYPE | Redisのエラー「WRONGTYPE」 | Key に予期しないデータ型が含まれています | データモデルまたはスクリプトの要件を修正してください。業務上の却下として扱わないでください。. |
| maxmemory によるメモリ使用量 | 書き込み操作により、スクリプトが中断される可能性があります | Redisは起動時にすでにメモリ制限を超えている | 盲目的に繰り返さないでください。redis.pcall では、安全で文書化されたエラー処理の仕組みを用意してください。. |
| BUSY | 他のコマンドに対する「BUSY」エラー応答 | スクリプトがbusy-reply-thresholdを超過しました | 負荷を軽減し、スクリプトのサイズを縮小する。書き込み処理の後、kill コマンドに依存しないようにする。. |
| 専門的な反対 | 記録されたステータス値 | 「上限に達した」または「残高が少なすぎる」など | ステータスを評価し、その取引を適切に却下する。. |
時点では maxmemory 処理の流れは、最初の書き込み操作によって決まります。Redisがすでに制限を超えている場合、メモリを大量に消費するコマンドを実行すると、 redis.call スクリプトを中止する;; redis.pcall Lua にエラーを返却し、意図的に設計されたエラー処理パスを必要とします。これにより、すでに実行された変更は元に戻されません。.
追加のメモリを必要としない最初の手順としては、例えば DEL 或いは LREM, 一方でスクリプトは実行を継続させることができます。その後の書き込み操作により、消費量は maxmemory 増加させる。次のような技術的な不具合など WRONGTYPE 或いは CROSSSLOT Redis Open Sourceでは、クラスタ機能が有効になっている場合、データモデルやキー設計の修正が必要となる一方、スクリプト自体のみが「安定」状態として機能的に承認されることができます。.
適切な使用事例を慎重に判断する
条件付き予約の場合、スクリプトで在庫を確認し、在庫数が不足している場合は予約を拒否し、成功した場合は残在庫数を返すことができます。その アトミック予約 ただし、これにはRedisのみが含まれます。決済、リレーショナルデータベース、電子メール、および外部APIについては、個別に調整が必要であり、必要に応じてバランス調整ロジックも必要となります。.
選択は、Redisのバージョンとデータモデルによって異なります。Redis Open Source 8.4以降では、 SET 条件付き設定と DELEX 個々の文字列キーの比較と削除を行います。Redis 8.4 以前、または条件がより複雑な場合には、 WATCH をもって MULTI/EXEC 別の方法:EXECの前に監視対象のキーが変更された場合、トランザクションは中止され、クライアントが再読み込みと再実行を行うかどうかを決定します。サーバー側で複数のコマンドやデータ構造(それらのビジネスルールを含む)を連携させる必要がある場合、短いLuaスクリプトが適しています。.
分散ロックについては、単一のコマンドだけでは不十分であり、Luaパターンも全体的なコンセプトとしては不十分である。 リース期間、プロセスの一時停止、障害、繰り返し、フェイルオーバー、およびマルチインスタンスのシナリオについては、個別に評価する必要があります。運用中のバージョンとそのセマンティクスがルール全体を網羅している場合は、ネイティブコマンドを優先してください。そうでない場合は、 WATCH また、エラー処理の仕様やビジネスロジックの配置場所に応じて、短いスクリプトを採用するか検討する必要があります。再利用可能なサーバーサイドロジックについては、Redis Functionが適している場合があります。. 読み取り専用スクリプト Redis 7.0 以降では、 EVAL_RO 或いは EVALSHA_RO 実行可能ですが、書き込みが行われないことが保証されているロジックの場合に限ります。.
出典および専門的な見解
調査状況:
調査およびバージョン情報:2026年9月23日。本記事では、オープンソース版のRedisについて解説し、Redis 7.0以降におけるEVALスクリプトとRedis Functionsの違いについて説明しています。実際に運用しているRedisのバージョンに合わせて、利用前にバージョンの制限や利用可能なコマンドを確認してください。.
https://redis.io/docs/latest/develop/programmability/eval-intro/
https://redis.io/docs/latest/develop/programmability/
https://redis.io/docs/latest/commands/eval/
https://redis.io/docs/latest/develop/using-commands/multi-key-operations/
https://redis.io/docs/latest/develop/using-commands/transactions/
https://redis.io/docs/latest/develop/programmability/functions-intro/
https://redis.io/docs/latest/commands/evalsha_ro/




