はじめに
私の基本ライブラリの 1 つで Redis との通信コストを削減するために、一連の操作を LUA スクリプトにカプセル化し、Redis が提供する EVAL コマンドを使用しました。操作を簡素化するため。
EVAL が提供できる機能:
LUA スクリプトで複数の操作をカプセル化できます。複数の Redis 命令がある場合、カプセル化その後、すべてのパラメーターを一度に Redis に送信して結果を取得します。
Redis は、Lua スクリプトの実行中に他のコマンドが挿入および実行されないことを保証し、アトミック性を提供します。
Redis は、スクリプトの SHA 値に従ってスクリプトをキャッシュします。キャッシュされたスクリプトは、Lua コードを再度送信する必要がなく、通信コストが削減されます。さらに、独自のコードで Lua スクリプトを変更すると、Redis の実行時にも最新のコードが使用されます。
「github.com/go-redis/redis」などの一般的な Go ライブラリをインポートして、次のコードを実装します。
Lua スクリプトの生成
// KEYS: key for record // ARGV: fieldName, currentUnixTimestamp, recordTTL // Update expire field of record key to current timestamp, and renew key expiration var updateRecordExpireScript = redis.NewScript(` redis.call("EXPIRE", KEYS[1], ARGV[3]) redis.call("HSET", KEYS[1], ARGV[1], ARGV[2]) return 1 `)
この変数が作成されると、Lua コードは実行されず、既存の Redis 接続が必要ありません。
Redis が提供する Lua スクリプト サポートには、デフォルトで KEYS と ARGV という 2 つの配列があります。KEYS は、スクリプトの実行時に渡されるいくつかのキー値を表し、ARGV は渡されるいくつかのパラメータを表します。 Lua コードは簡潔で読みにくいものにする必要があるため、これらのパラメータに対していくつかのコメントを記述することをお勧めします。
注: 上記のコードでは、複数の行にわたって `` が使用されています。 ` が配置されている行は空白のキャリッジ リターンも行とみなされますが、エラーを報告するときはコードの行番号を読み違えないようにしてください。
Lua スクリプトの実行
updateRecordExpireScript.Run(c.Client, []string{recordKey(key)}, expireField, time.Now().UTC().UnixNano(), int64(c.opt.RecordTTL/time.Second)).Err()
実行時、Run はまず EVALSHA を介してキャッシュを通じてスクリプトを実行しようとします。キャッシュがない場合は、EVAL を使用して実行すると、Lua スクリプト全体が Redis に渡されます。
Lua スクリプトの制限
Redis では、OS などの追加パッケージの導入は提供されません。利用できるのは Redis パッケージのみです。
Lua スクリプトは関数内で実行され、すべての変数はローカルを使用して宣言する必要があります。
return が複数の値を返す場合、Redis は最初の
スクリプト内の入力制限のみを示します
スクリプトが nil を返したときに Go で得られるものはい err = redis.Nil
(Get が値を見つけられないのと同じ)
スクリプトが false を返した場合、Go で取得されるものは nil であり、スクリプトは true を返します。Go で取得されるのは int64 型です。1
スクリプトが {"ok": ...} を返すと、Go で取得されるのはステータス型です。 redis (true/false)
スクリプトが {"err": ...} を返すと、Go で err 値が取得されます。return redis を使用することもできます。 error_reply("My Error")
Achieved
スクリプトが数値型を返すと、Go で取得されるのは int64 型です
スクリプトでは、必要に応じて、受信 KEYS/ ARGV の値を文字列タイプから数値タイプに変換するには、to_number 関数を使用する必要があります。
スクリプトの実行時間が長いですか?
Lua スクリプトの実行中は、他の操作によるデータの汚染を避けるため、この期間中は他のコマンドを実行できず、実行が完了するまで他のリクエストを続行できません。 Lua スクリプトの実行時間が lua-time-limit を超えると、他のリクエストは、SCRIPT KILL (スクリプトを強制終了) または SHUTDOWN NOSAVE (結果を保存せずに Redis を閉じる) でない限り、Busy エラーを受け取ります。
詳細ここでは主に私が Go を使用した経験に基づいていくつかの概要を説明します。 https://redis.io/commands/eval
より「複雑な」スクリプト。キー値を取得するときに、その値に頻繁にアクセスするとライフサイクルが延長される必要があります。また更新時間も比較する必要があり、更新が必要ない場合は取得した値をそのまま返しますが、そうでない場合はredis.Nil
// KEYS: rec:key, key // ARGV: currentUnixTimestamp, hotHit, recordTTL, ttl // When there's a hit, var fetchRecordScript = redis.NewScript(local value = redis.call("GET", KEYS[2]) if(value == nil) then return nil end local hit = redis.call("HINCRBY", KEYS[1], "hit", 1) redis.call("EXPIRE", KEYS[1], ARGV[3]) local minHotHit = tonumber(ARGV[2]) local keyTTL = tonumber(ARGV[4]) if(hit > minHotHit)then keyTTL = keyTTL * 2 end redis.call("EXPIRE", KEYS[2], keyTTL) local expire = tonumber(redis.call("HGET", KEYS[1], "expire")) local unixTime = tonumber(ARGV[1]) if(expire == nil or expire < unixTime) then return nil else return value end) // KEYS: key for record // ARGV: fieldName, currentUnixTimestamp, recordTTL // Update expire field of record key to current timestamp, and renew key expiration var updateRecordExpireScript = redis.NewScript(redis.call("EXPIRE", KEYS[1], ARGV[3]) redis.call("HSET", KEYS[1], ARGV[1], ARGV[2]) return 1)を返します。
以上がGo言語のLuaスクリプトでRedisを操作する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。