すべてのプロダクト
Search
ドキュメントセンター

Tair (Redis® OSS-Compatible):Luaスクリプトの構文と一般的なエラーの解決策

最終更新日:Dec 06, 2024

Tair (Redis OSS-compatible) インスタンスはLuaコマンドをサポートしています。 Luaスクリプトを使用して、比較設定 (CAS) コマンドを効率的に処理できます。 これにより、Tair (Redis OSS互換) のパフォーマンスが向上し、機能の実装が簡素化されます。 このトピックでは、Luaスクリプトの構文と使用方法について説明します。

注意事項

Luaスクリプトに関連するコマンドは、データ管理 (DMS) コンソールでは使用できません。 DMSの詳細については、「概要」をご参照ください。 クライアントまたはredis-cliを使用してインスタンスに接続し、Luaスクリプトを使用できます。

構文

コマンド

構文

説明

EVAL

EVALスクリプトnumkeys [key [key ...]]] [arg [arg ...]]

パラメーターを含む特定のスクリプトを実行し、出力を返します。

構文で使用されるパラメーターを次の表に示します。

  • script: Luaスクリプト。

  • numkeys: KEYS配列内の引数の数。 この数は、非負の整数である。

  • KEYS[]: 引数としてスクリプトに渡すキー。

  • ARGV[]: スクリプトに渡す追加の引数。 KEYS[] およびARGV[] パラメータのインデックスは1から始まります。

説明
  • EVALコマンドは、script LOADコマンドと同様の方法でスクリプトをスクリプトキャッシュにロードします。

  • KEYS[] パラメーターとARGV[] パラメーターを混在して使用または悪用すると、インスタンス、特にクラスターインスタンスが異常に実行される可能性があります。 詳細については、「クラスターインスタンスのLuaスクリプトの制限」をご参照ください。

  • パラメータをLuaスクリプトにエンコードするのではなく、KEYS[] およびARGV[] パラメータで値を渡してLuaスクリプトを呼び出すことを推奨します。 そうしないと、Lua仮想マシンのメモリ使用量が増加し、できるだけ早く減らすことができません。 最悪のシナリオでは、インスタンスでメモリ不足 (OOM) エラーが発生し、データが失われます。

EVALSHA

EVALSHA sha1 numkeys key [key ...] arg [arg ...]

SHA1ダイジェストによってキャッシュされたスクリプトを評価し、スクリプトを実行します。

EVALSHAコマンドを使用するときにスクリプトがTair (Redis OSS互換) にキャッシュされていない場合、Tair (Redis OSS互換) はNOSCRIPTエラーを返します。 EVALまたはscript LOADコマンドを使用してスクリプトをTair (Redis OSS互換) にキャッシュし、再試行します。 詳細については、「NOSCRIPTエラーの処理」をご参照ください。

スクリプトロード

SCRIPTロードスクリプト

指定されたスクリプトをキャッシュし、スクリプトのSHA1ダイジェストを返します。

スクリプトが存在

SCRIPT EXISTSスクリプト [script ...]

対応するSHA1ダイジェストを使用して、スクリプトキャッシュ内の1つ以上のスクリプトの存在に関する情報を返します。 特定のスクリプトが存在する場合、値1が返されます。 そうでなければ、0の値が返される。

SCRIPT KILL

SCRIPT KILL

実行中のLuaスクリプトを終了します。

SCRIPT FLUSH

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構文に制限があります。

次の表に、エラーコードと原因を示します。

カテゴリ

エラーコード

説明

クラスターアーキテクチャ

-ERR for redisクラスター、eval/evalshaキーの数は負またはゼロにすることはできません \r\n

Luaスクリプトを実行するときにキーを含める必要があります。 プロキシはキーを使用して、Luaスクリプトの転送先のシャードを決定します。

# Example of valid command usage:
EVAL "return redis.call('get', KEYS[1])" 1 fooeval

# Example of invalid command usage:
EVAL "return redis.call('get', 'foo')" 0

-ERR 'xxx' コマンドキーは同じスロットにある必要があります

Luaスクリプトの複数のキーは同じスロットに属している必要があります。

# Example of valid command usage:
EVAL "return redis.call('mget', KEYS[1], KEYS[2])" 2 foo {foo}bar

# Example of invalid command usage:
EVAL "return redis.call('mget', KEYS[1], KEYS[2])" 2 foo foobar

プロキシLua構文チェック

(このチェックを無効にするには、script_check_enableパラメーターを0に設定できます)

-ERR bad luaスクリプトfor redis cluster, neste d redis.ca ll/redis.pcall

ネストされた呼び出しはサポートされません。 ローカル変数を使用してLuaスクリプトを呼び出すことができます。

# Example of valid command usage:
EVAL "local value = redis.call('GET', KEYS[1]); redis.call('SET', KEYS[2], value)" 2 foo bar

# Example of invalid command usage:
EVAL "redis.call('SET', KEYS[1], redis.call('GET', KEYS[2]))" 2 foo bar

-ERR bad luaスクリプトfor redis cluster, first parameter o f redis.ca ll/redis.pcallは単一のリテラル文字列である必要があります

redis.ca llまたはredis.pcall関数でLuaスクリプトを使用する場合、最初に指定するパラメーターは、実行するコマンドを表す文字列リテラルである必要があります。

# Example of valid command usage:
eval "redis.call('GET', KEYS[1])" 1 foo

# Example of invalid command usage:
eval "local cmd = 'GET'; redis.call(cmd, KEYS[1])" 1 foo

-ERR bad luaスクリプトfor redis cluster、スクリプトが使用するすべてのキーはkeys配列 \r\nを使用して渡す必要があります

Luaスクリプトは、redis.ca llまたはredis.pcall関数を使用してコマンドを実行します。 すべてのキーはkeys配列を使用して指定する必要があり、Lua変数で置き換えることはできません。

説明

この制限は、Redis 5.0を実行し、マイナーバージョンが5.0.8より前のRedis Open-Source Editionインスタンスと、Redis 4.0以前を実行するインスタンスにのみ適用されます。

# Example of valid command usage:
EVAL "return redis.call('mget', KEYS[1], KEYS[2])" 2 foo {foo}bar

# Example of invalid command usage:
EVAL "return redis.call('mget', KEYS[1], '{foo}bar')" 1 foo                      # This command is invalid because the '{foo}bar' key must be specified by using the KEYS array. 
EVAL "local i = 2 return redis.call('mget', KEYS[1], KEYS[i])" 2 foo {foo}bar    # This command is invalid because the index of keys consists of variables, which is not allowed for an instance in proxy mode. Instances in direct connection mode are not subject to this limit. 
EVAL "return redis.call('mget', KEYS[1], ARGV[1])" 1 foo {foo}bar                # This command is invalid because ARGV[1] cannot be specified as a key. 

-ERR bad luaスクリプトfor redis cluster、スクリプトが使用するすべてのキーはkeys配列を使用して渡される必要があります。include destination、KEYSは式に含まれません

ZUNIONSTOREおよびZINTERSTOREコマンドの宛先パラメーターは、KEYS配列を使用して指定する必要があります。

説明

この制限は、Redis 5.0を実行し、マイナーバージョンが5.0.8より前のRedis Open-Source Editionインスタンスと、Redis 4.0以前を実行するインスタンスにのみ適用されます。

-redisクラスターのERR bad luaスクリプト、ZUNIONSTORE/ZINTERSTORE numkeysパラメーターは、式ではなく単一の数値である必要があります

ZUNIONSTOREコマンドとZINTERSTOREコマンドのnumkeysパラメーターは定数ではありません。

説明

この制限は、Redis 5.0を実行し、マイナーバージョンが5.0.8より前のRedis Open-Source Editionインスタンスと、Redis 4.0以前を実行するインスタンスにのみ適用されます。

-redisクラスターのERR不良luaスクリプト、ZUNIONSTORE/ZINTERSTORE numkeys値が整数または範囲外ではありません

ZUNIONSTOREコマンドとZINTERSTOREコマンドのnumkeysパラメーターは数値ではありません。

説明

この制限は、Redis 5.0を実行し、マイナーバージョンが5.0.8より前のRedis Open-Source Editionインスタンスと、Redis 4.0以前を実行するインスタンスにのみ適用されます。

-ERR bad luaスクリプトfor redis cluster, ZUNIONSTORE/ZINTERSTOREスクリプトが使用するすべてのキーはkeys配列を使用して渡される必要があります

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以前を実行するインスタンスにのみ適用されます。

-ERR bad luaスクリプトfor redis cluster、スクリプトが使用するすべてのキーはkeys配列を使用して渡される必要があり、KEYSは式に含まれない必要があります。sort command store keyが要件を満たしていません

SORTコマンドのキーは、keys配列を使用して指定する必要があります。

説明

この制限は、Redis 5.0を実行し、マイナーバージョンが5.0.8より前のRedis Open-Source Editionインスタンスと、Redis 4.0以前を実行するインスタンスにのみ適用されます。

読み取り /書き込み権限

-ERR Writeコマンドは読み取り専用スクリプトからは許可されません

EVAL_ROコマンドを使用して送信されるLuaスクリプトには、書き込みコマンドは含まれません。

-ERR bad writeコマンドが書き込み権限なし

読み取り専用アカウントから送信されるLuaスクリプトには、書き込みコマンドを含めることはできません。

サポートされていないコマンド

-ERRスクリプトデバッグがサポートされていません

SCRIPT DEBUGコマンドはプロキシモードではサポートされていません。

-ERR bad luaスクリプトfor redis cluster, redis.ca ll/pcall unkown redisコマンドxxx

Luaスクリプトには、プロキシモードではサポートされないコマンドが含まれます。 詳細については、「クラスターインスタンスおよび読み書き分離インスタンスでサポートされているコマンドの制限」をご参照ください。

Lua構文

  • -ERRの悪いluaスクリプトredisクラスター、redis.ca ll/pcall expect '('

  • -ERR bad luaスクリプトfor redis cluster, redis.ca ll/redis.pcall定義が完了していない, expect ')'

Lua構文エラー: redis.ca ll関数の後に (および) の完全なセットが続く必要があります。

-redisクラスターのERR不良luaスクリプト、ZUNIONSTORE/ZINTERSTOREには少なくとも1つの入力キーが必要です

ZUNIONSTOREコマンドとZINTERSTOREコマンドのnumkeysパラメーターは0より大きくなければなりません。

-redisクラスターのERR不良luaスクリプト、ZUNIONSTORE/ZINTERSTOREキーカウント <numkeys

ZUNIONSTOREコマンドとZINTERSTOREコマンドのキーの数がnumkeys値よりも少ない。

-redisクラスターのERR不良luaスクリプト、xread/xreadgroupコマンド構文エラー

XREADまたはXREADGROUPコマンドの構文が正しくありません。 パラメータの数を確認してください。

-redisクラスターのERR不良luaスクリプト、xread/xreadgroupコマンド構文エラー、ストリームを指定する必要があります

streamsパラメーターは、XREADコマンドとXREADGROUPコマンドで必要です。

-redisクラスターのERR不良luaスクリプト、ソートコマンド構文エラー

SORTコマンドの構文が正しくありません。