Tair (Redis OSS-compatible) インスタンスはLuaコマンドをサポートしています。 Luaスクリプトを使用して、比較設定 (CAS) コマンドを効率的に処理できます。 これにより、Tair (Redis OSS互換) のパフォーマンスが向上し、機能の実装が簡素化されます。 このトピックでは、Luaスクリプトの構文と使用方法について説明します。
注意事項
Luaスクリプトに関連するコマンドは、データ管理 (DMS) コンソールでは使用できません。 DMSの詳細については、「概要」をご参照ください。 クライアントまたはredis-cliを使用してインスタンスに接続し、Luaスクリプトを使用できます。
構文
コマンド | 構文 | 説明 |
EVAL |
| パラメーターを含む特定のスクリプトを実行し、出力を返します。 構文で使用されるパラメーターを次の表に示します。
説明
|
EVALSHA |
| SHA1ダイジェストによってキャッシュされたスクリプトを評価し、スクリプトを実行します。 EVALSHAコマンドを使用するときにスクリプトがTair (Redis OSS互換) にキャッシュされていない場合、Tair (Redis OSS互換) はNOSCRIPTエラーを返します。 EVALまたはscript LOADコマンドを使用してスクリプトをTair (Redis OSS互換) にキャッシュし、再試行します。 詳細については、「NOSCRIPTエラーの処理」をご参照ください。 |
スクリプトロード |
| 指定されたスクリプトをキャッシュし、スクリプトのSHA1ダイジェストを返します。 |
スクリプトが存在 |
| 対応するSHA1ダイジェストを使用して、スクリプトキャッシュ内の1つ以上のスクリプトの存在に関する情報を返します。 特定のスクリプトが存在する場合、値1が返されます。 そうでなければ、0の値が返される。 |
SCRIPT KILL |
| 実行中のLuaスクリプトを終了します。 |
SCRIPT FLUSH |
| 現在のインスタンスのスクリプトキャッシュからすべてのLuaスクリプトを削除します。 |
コマンドの詳細については、
Redis公式ウェブサイト。次のサンプルコードは、特定のRedisコマンドの例を示します。 次のコマンドを実行する前に、SET foo values_test
コマンドを実行します。
サンプルEVALコマンド:
EVAL "return redis.call('GET', KEYS[1])" 1 foo
サンプル出力:
"value_test"
サンプルSCRIPT LOADコマンド:
SCRIPT LOAD "return redis.call('GET', KEYS[1])"
サンプル出力:
"620cd258c2c9c88c9d10db67812ccf663d96bdc6"
サンプルEVALSHAコマンド:
EVALSHA 620cd258c2c9c88c9d10db67812ccf663d96bdc6 1 foo
サンプル出力:
"value_test"
サンプルSCRIPT EXISTSコマンド:
SCRIPT EXISTS 620cd258c2c9c88c9d10db67812ccf663d96bdc6 ffffffffffffffffffffffffffffffffffffffff
サンプル出力:
1) (integer) 1 2) (integer) 0
サンプルSCRIPT FLUSHコマンド:
警告このコマンドは、キャッシュされたすべてのLuaスクリプトをインスタンスから削除します。 このコマンドを実行する前に、必ずLuaスクリプトをバックアップしてください。
SCRIPT FLUSH
サンプル出力:
OK
メモリとネットワークのオーバーヘッドを削減
問題の説明:
同じ目的を果たす多数のスクリプトがインスタンスにキャッシュされます。 これらのスクリプトは大量のメモリを占有し、メモリ不足 (OOM) エラーを引き起こす可能性があります。 無効な使用の例:
EVAL "return redis.call('set', 'k1', 'v1')" 0
EVAL "return redis.call('set', 'k2', 'v2')" 0
解決策:
メモリ使用量を減らすために、パラメータを定数としてLuaスクリプトに渡さないでください。
# The following commands serve the same purposes as the preceding sample commands but cache scripts only once. EVAL "return redis.call('set', KEYS[1], ARGV[1])" 1 k1 v1 EVAL "return redis.call('set', KEYS[1], ARGV[1])" 1 k2 v2
次のコマンド構文を使用して、メモリとネットワークのオーバーヘッドを減らします。
SCRIPT LOAD "return redis.call('set', KEYS[1], ARGV[1])" # After this command is run, the following output is returned: "55b22c0d0cedf3866879ce7c854970626dcef0c3". EVALSHA 55b22c0d0cedf3866879ce7c854970626dcef0c3 1 k1 v1 EVALSHA 55b22c0d0cedf3866879ce7c854970626dcef0c3 1 k2 v2
Luaスクリプトキャッシュをフラッシュする
問題の説明:
Luaスクリプトキャッシュがインスタンスのメモリを占有するため、インスタンスの使用済みメモリが予想よりも高くなる可能性があります。 インスタンスの使用メモリが上限に近づくか、上限を超えると、OOMエラーが返されます。 エラー例:
-OOM command not allowed when used memory > 'maxmemory'.
解決策:
クライアントでscript Flushコマンドを実行して、Luaスクリプトキャッシュをフラッシュします。 FLUSHALLコマンドとは異なり、SCRIPT FLUSHコマンドは同期しています。 インスタンスが多数のLuaスクリプトをキャッシュしている場合、SCRIPT FLUSHコマンドはインスタンスを長期間ブロックし、関連するインスタンスが使用できなくなる可能性があります。 作業は慎重に行ってください。 そのため、ピーク時間外に操作を実行することを推奨します。
コンソールで データの消去 をクリックすると、データはクリアできますが、Luaスクリプトキャッシュはフラッシュできません。
過剰なメモリを占有する可能性があるLuaスクリプトを記述しないでください。 さらに、大量のデータを含むLuaスクリプトを記述しないでください。 そうしないと、メモリ使用量が大幅に増加し、OOMエラーが発生する可能性があります。 メモリ使用量を減らすために、volatile-lruポリシーを使用してdata evictionを有効にすることを推奨します。 デフォルトでは、インスタンスでデータ削除が有効になっています。 ただし、データの削除が有効になっているかどうかに関係なく、インスタンスはLuaスクリプトキャッシュを削除しません。
NOSCRIPTエラーの処理
問題の説明:
EVALSHAコマンドの使用時にスクリプトがキャッシュされていない場合、TairはNOSCRIPTエラーを返します。 エラー例:
(error) NOSCRIPT No matching script. Please use EVAL.
解決策:
EVALまたはSCRIPT LOADコマンドを実行してスクリプトをキャッシュし、再試行します。 インスタンスの移行や構成の変更などの特定のシナリオでは、Tair (Redis OSS互換) がLuaスクリプトの永続性と複製可能性を保証できないため、インスタンスはLuaスクリプトキャッシュをフラッシュします。 この場合、クライアントはエラーを処理できる必要があります。 詳細については、「スクリプトのキャッシュ、永続化、レプリケーション」をご参照ください。
次のサンプルPythonコードは、NOSCRIPTエラーを処理するメソッドを示しています。 サンプルコードは、Luaスクリプトを使用して文字列の先頭に追加します。
redis-pyを使用してエラーを処理することもできます。 redis-pyは、NOSCRIPTエラーのcatchステートメントなど、TairのLuaスクリプト (Redis OSS互換) の判断ロジックをカプセル化するScriptクラスを提供します。
import redis
import hashlib
# strin specifies a string in Lua scripts. The following function returns the SHA1 value of strin in the string format.
def calcSha1(strin):
sha1_obj = hashlib.sha1()
sha1_obj.update(strin.encode('utf-8'))
sha1_val = sha1_obj.hexdigest()
return sha1_val
class MyRedis(redis.Redis):
def __init__(self, host="localhost", port=6379, password=None, decode_responses=False):
redis.Redis.__init__(self, host=host, port=port, password=password, decode_responses=decode_responses)
def prepend_inLua(self, key, value):
script_content = """\
local suffix = redis.call("get", KEYS[1])
local prefix = ARGV[1]
local new_value = prefix..suffix
return redis.call("set", KEYS[1], new_value)
"""
script_sha1 = calcSha1(script_content)
if self.script_exists(script_sha1)[0] == True: # Check whether the script is already cached in Tair (Redis OSS-compatible).
return self.evalsha(script_sha1, 1, key, value) # If the script is already cached, the EVALSHA command is used to run the script.
else:
return self.eval(script_content, 1, key, value) # Otherwise, use the EVAL command to run the script. Note that the EVAL command can cache scripts. Another method is to use the SCRIPT LOAD and EVALSHA commands.
r = MyRedis(host="r-******.redis.rds.aliyuncs.com", password="***:***", port=6379, decode_responses=True)
print(r.prepend_inLua("k", "v"))
print(r.get("k"))
Luaスクリプトのタイムアウトの処理
問題の説明:
Luaスクリプトはインスタンスでアトミックに実行されるため、低速のLuaリクエストはインスタンスをブロックする可能性があります。 1つのLuaスクリプトでインスタンスを最大5秒間ブロックできます。 5秒後、スクリプトの実行が完了するまで、インスタンスは他のコマンドのBUSYエラーを返します。
BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE.
解決策:
SCRIPT KILLコマンドを実行してLuaスクリプトを終了するか、Luaスクリプトの実行が完了するまで待ちます。
説明低速Luaスクリプトが実行されている最初の5秒間は、インスタンスがブロックされているため、script KILLコマンドは有効になりません。
instancveが長時間ブロックされないようにするには、Luaスクリプトを作成するときにLuaスクリプトの実行に必要な時間を見積もり、無限ループを確認し、必要に応じてLuaスクリプトを分割することをお勧めします。
問題の説明:
Luaスクリプトがデータセットに対して書き込みコマンドを実行している場合、script KILLコマンドは有効になりません。 エラー例:
(error) UNKILLABLE Sorry the script already executed write commands against the dataset. You can either wait the script termination or kill the server in a hard way using the SHUTDOWN NOSAVE command.
解決策:
コンソールの インスタンス一覧 ページで、管理するインスタンスを見つけ、[操作] 列の 再起動 をクリックします。
スクリプトのキャッシュ、永続化、およびレプリケーション
問題の説明:
Tair (Redis OSS互換) は、インスタンスが再起動されていない場合、またはインスタンスに対してSCRIPT FLUSHコマンドが実行されていない場合、インスタンスで実行されたLuaスクリプトをキャッシュし続けます。 ただし、Tair (Redis OSS互換) は、インスタンスの移行、構成の変更、バージョンのアップグレード、インスタンスの切り替えなどのシナリオでは、Luaスクリプトの永続性や現在のノードから他のノードへのLuaスクリプトの同期を保証できません。
解決策:
すべてのLuaスクリプトをオンプレミスデバイスに保存します。 必要に応じてEVALまたはSCRIPT LOADコマンドを使用して、Tair (Redis OSS互換) でLuaスクリプトを再キャッシュします。 これにより、インスタンスの再起動または高可用性 (HA) の切り替え中にLuaスクリプトがクリアされたときにNOSCRIPTエラーが発生するのを防ぎます。
クラスターインスタンスのLuaスクリプトの制限
実行アトミック性を確保するために、Luaスクリプトは分割できず、クラスターインスタンス内の1つのシャードでのみ実行できます。 ほとんどの場合、キーはどのシャードLuaスクリプトにルーティングされるかを決定するために使用されます。 したがって、クラスターインスタンスでLuaスクリプトを実行するときは、少なくとも1つのキーを指定する必要があります。 複数のキーを読み書きする場合は、1つのLuaスクリプトのキーが同じスロットに属している必要があります。 それ以外の場合、異常な実行結果が返されます。 キーを持たないLuaスクリプト (keys、SCAN、FLUSHDBなど) は正常に実行できます。 ただし、1つのシャードのデータのみが返されます。 この制限は、クラスターインスタンスのアーキテクチャによって発生します。
1つのノードでSCRIPT LOADコマンドを実行すると、Luaスクリプトが他のノードに格納されない場合があります。
プロキシモードでのカスタムLuaスクリプトエラーのエラーコードと原因
プロキシは、構文チェックを実行して複数のスロットに属するキーを識別し、事前に例外をスローしてトラブルシューティングを支援します。 プロキシは、Lua仮想マシンとは異なるチェック方法を使用します。 これにより、プロキシモードでのLuaスクリプトの実行に追加の制限が加えられます。 たとえば、UNPACKコマンドはサポートされておらず、MULTIおよびEXECトランザクションではEVAL、EVALSHA、およびSCRIPTコマンドはサポートされていません。 script_check_enableパラメーターを設定して、プロキシモードでLua構文の追加チェックを無効にすることもできます。
script_check_enableパラメーターを0に設定すると、インスタンスにどのように影響しますか。
インスタンスがRedis 5.0 (5.0.8より前のマイナーバージョン) および4.0以前と互換性がある場合は、チェックを無効にしないことを推奨します。 そうしないと、スクリプトが正常に実行されない場合、正常な結果が返される場合があります。
他のバージョンのチェックを無効にすると、プロキシはLua構文をチェックしなくなりますが、データノードは引き続きLua構文をチェックします。
読み書き分離アーキテクチャインスタンスのreadonly_lua_route_ronode_enableパラメーターが1に設定されている場合、プロキシはLuaスクリプトに読み取り専用コマンドのみが含まれているかどうかを確認し、読み取り専用ノードに転送するかどうかを判断します。 このチェックロジックでは、Lua構文に制限があります。
次の表に、エラーコードと原因を示します。
カテゴリ | エラーコード | 説明 |
クラスターアーキテクチャ |
| Luaスクリプトを実行するときにキーを含める必要があります。 プロキシはキーを使用して、Luaスクリプトの転送先のシャードを決定します。
|
| Luaスクリプトの複数のキーは同じスロットに属している必要があります。
| |
プロキシLua構文チェック (このチェックを無効にするには、script_check_enableパラメーターを0に設定できます) |
| ネストされた呼び出しはサポートされません。 ローカル変数を使用してLuaスクリプトを呼び出すことができます。
|
| redis.ca llまたはredis.pcall関数でLuaスクリプトを使用する場合、最初に指定するパラメーターは、実行するコマンドを表す文字列リテラルである必要があります。
| |
| Luaスクリプトは、redis.ca llまたはredis.pcall関数を使用してコマンドを実行します。 すべてのキーはkeys配列を使用して指定する必要があり、Lua変数で置き換えることはできません。 説明 この制限は、Redis 5.0を実行し、マイナーバージョンが5.0.8より前のRedis Open-Source Editionインスタンスと、Redis 4.0以前を実行するインスタンスにのみ適用されます。
| |
| ZUNIONSTOREおよびZINTERSTOREコマンドの宛先パラメーターは、KEYS配列を使用して指定する必要があります。 説明 この制限は、Redis 5.0を実行し、マイナーバージョンが5.0.8より前のRedis Open-Source Editionインスタンスと、Redis 4.0以前を実行するインスタンスにのみ適用されます。 | |
| ZUNIONSTOREコマンドとZINTERSTOREコマンドのnumkeysパラメーターは定数ではありません。 説明 この制限は、Redis 5.0を実行し、マイナーバージョンが5.0.8より前のRedis Open-Source Editionインスタンスと、Redis 4.0以前を実行するインスタンスにのみ適用されます。 | |
| ZUNIONSTOREコマンドとZINTERSTOREコマンドのnumkeysパラメーターは数値ではありません。 説明 この制限は、Redis 5.0を実行し、マイナーバージョンが5.0.8より前のRedis Open-Source Editionインスタンスと、Redis 4.0以前を実行するインスタンスにのみ適用されます。 | |
| ZUNIONSTOREおよびZINTERSTOREコマンドのすべてのキーは、keys配列を使用して指定する必要があります。 説明 この制限は、Redis 5.0を実行し、マイナーバージョンが5.0.8より前のRedis Open-Source Editionインスタンスと、Redis 4.0以前を実行するインスタンスにのみ適用されます。 | |
-redisクラスターのERR不良luaスクリプト、XREAD/XREADGROUPスクリプトが使用するすべてのキーは、keys配列を使用して渡される必要があります | XREADおよびXREADGROUPコマンドのすべてのキーは、keys配列を使用して指定する必要があります。 説明 この制限は、Redis 5.0を実行し、マイナーバージョンが5.0.8より前のRedis Open-Source Editionインスタンスと、Redis 4.0以前を実行するインスタンスにのみ適用されます。 | |
| SORTコマンドのキーは、keys配列を使用して指定する必要があります。 説明 この制限は、Redis 5.0を実行し、マイナーバージョンが5.0.8より前のRedis Open-Source Editionインスタンスと、Redis 4.0以前を実行するインスタンスにのみ適用されます。 | |
読み取り /書き込み権限 |
| EVAL_ROコマンドを使用して送信されるLuaスクリプトには、書き込みコマンドは含まれません。 |
| 読み取り専用アカウントから送信されるLuaスクリプトには、書き込みコマンドを含めることはできません。 | |
サポートされていないコマンド |
| SCRIPT DEBUGコマンドはプロキシモードではサポートされていません。 |
| Luaスクリプトには、プロキシモードではサポートされないコマンドが含まれます。 詳細については、「クラスターインスタンスおよび読み書き分離インスタンスでサポートされているコマンドの制限」をご参照ください。 | |
Lua構文 |
| Lua構文エラー: |
| ZUNIONSTOREコマンドとZINTERSTOREコマンドのnumkeysパラメーターは0より大きくなければなりません。 | |
| ZUNIONSTOREコマンドとZINTERSTOREコマンドのキーの数がnumkeys値よりも少ない。 | |
| XREADまたはXREADGROUPコマンドの構文が正しくありません。 パラメータの数を確認してください。 | |
| streamsパラメーターは、XREADコマンドとXREADGROUPコマンドで必要です。 | |
| SORTコマンドの構文が正しくありません。 |