1. ホーム
  2. スクリプト・コラム
  3. ルア

Luaを使ったRedisコマンドのカスタマイズを解説

2022-01-09 20:03:09

プリアンブル

非常に成功したデータベースとして、Redisは豊富なデータ型とコマンドを提供し、多くのキャッシュ操作を簡単かつ効率的に行うことができます。しかし、解決しなければならない特別な問題やニーズが常に存在し、そのために独自のRedisデータ構造とコマンドをカスタマイズしなければならない場合があります。

Redisコマンドの問題点

スレッドの安全性に関する問題

Redisがシングルスレッドであることは周知の事実ですが、どのようにして スレッドセーフ の問題ですか?

通常、スレッドセーフの問題として理解されているのは、シングルプロセス、マルチスレッドモデル内で、複数のスレッドがプロセス内共有メモリを操作することによって引き起こされるデータリソースのオーバーフローです。Redisのスレッドセーフの問題は、Redisサーバーの内部から発生するものではありません。

{Redisは、データサーバーとして、同等です。 データサーバとしてのRedisは、複数のクライアントの共有メモリに相当し、複数のクライアントは同一プロセス内の複数のスレッドに相当し、複数のクライアント間で優れたデータ同期戦略がない場合、スレッドセーフのような問題が発生する可能性があります。

典型的なシナリオは、以下の通りです。

  • Redisは、user5277=idleの中にユーザーの状態を保存します。
  • クライアント接続Aは、ユーザーの状態を読み取り、アイドル状態を取得する status = get("user5277").
  • {を使用します。 クライアント接続Bもユーザーステータスを読み取ります。
  • クライアント接続Aは、ユーザーのタスクをスケジュールし、Redisのユーザーステータスをbusyに設定します set("user5277", "busy").
  • クライアント接続Bもユーザーをビジー状態に設定する。
  • {を使用します。 ただし、ユーザーには同時に2つのタスクが割り当てられている。

この問題の原因は、Redisはシングルスレッドであるため、コマンドのシリアライズが保証されているものの、実行効率が高く、複数のクライアントのコマンド間でリクエストを同期させないことで、コマンドの順序も誤ってしまうことにあります。

もちろん、この問題はユーザーステートにロックを追加し、1クライアントだけが同時にユーザーステートを操作できるようにすることで解決できる。しかし、ロックを追加するには、ロックの粒度やデッドロックなどを考慮する必要があり、プログラムが複雑になることは間違いなく、保守性にも欠ける。

効率性の問題

{レディス Redisは非常に効率の良いインメモリデータサーバーで、コマンドの実行速度も非常に速いです。 {答えはネットワークです。 答えはネットワークです。Webでご存知のように、効率の最適化はネットワークから始まります。サーバーサイドはコードやデータベースの最適化で、ネットワーク接続ほどではなく、最も効果的なネットワークの最適化はリクエストの数を減らすことです。メモリアクセス時間の実行が100ns程度であるのに対し、別室間の往復は500000ns程度必要であることを知る必要があり、そのギャップは想像に難くない。

{Redisは1台のマシン内で極めて効率的である。 Redisは1台のマシン内では極めて効率的ですが、産業界のデプロイメントではサーバーとRedisを同じマシンに置くことはないので、効率のボトルネックにぶつかるとしたら、それはネットワークなのです。

典型的なシナリオは、Redisからデータを読み取り、そのデータをキーとして別のデータを読み取るというものです。そのため、ネットワークは2回往復することになります。

この問題の原因は、Redisの通常のコマンドにはサーバーサイドの計算機能がなく、サーバー上で複合的なコマンド操作を行うことができないからです。Redisにはパイプライン機能があり、依存関係のない複数のコマンドのリクエストとレスポンスが必要です。相互に依存する複数のコマンドを簡略化したい場合は、データをクライアントに引き戻し、クライアントが処理してからRedisにリクエストするしかない。

まとめると、Redisをより効率的かつ簡単に利用するためには、いくつかのコマンドを自分たちで「カスタマイズ」する必要があるということです。

Luaのインライン実装

幸いなことに、RedisにはLuaの実行環境が組み込まれており、Luaスクリプトの実行をサポートしています。Luaスクリプトを実行することで、複数のコマンドを1つのLuaスクリプトに合成することができ、前述のRedisコマンドの順序付けやRedisサーバーサイドの計算をLuaスクリプトで実現することが可能です。

ルア

Luaは、クリーンで軽量、拡張性の高いスクリプト言語であり、以下のような機能を備えています。

  • 軽量:ソースパッケージはコアライブラリのみで、非常に小さくコンパイルされています。
  • 効率的:ANSI Cで書かれており、起動と実行が速い。
  • 組み込み型:様々なプログラミング言語やシステムで動作するように組み込むことができ、静的言語の柔軟性を向上させることができる。例えばOpenRestyは、Luaをnginxに埋め込んで実行する。

しかも、構文を気にする必要はまったくない。Luaの構文はとてもシンプルなので、数分で使えるようになります。

実行手順

バージョン2.6以降、RedisはLua環境の構築、Luaライブラリのロード、Redisグローバルテーブルの定義、redis.pcallなどのRedisコマンドの格納を行い、Luaスクリプトの実行準備を行うことで起動するようになった。

典型的なLuaスクリプトの実行手順は以下の通りです。

  1. スクリプトが実行されたかどうかを確認し、実行されていない場合はスクリプトのsha1チェックサムを使ってLua関数を生成します。
  2. タイムアウト、エラー処理フックを関数にバインドします。
  3. LuaでRedisコマンドを実行するための擬似クライアントを作成します。
  4. 擬似クライアントの戻り値を処理し、最終的にクライアントに返す。

相互作用のタイミングは次のとおりです。

画像

Luaスクリプトは疑似クライアントを使用していますが、Redisは通常のクライアントと同様に扱い、実行されたRedisコマンドのrdb aof master-slaveレプリケーションなどの操作を行います。

使用方法

LuaスクリプトはRedisのEVAL、EVALSHAコマンドで使用することができます。

{EVAL EVALは、Luaスクリプトを1回だけ実行する場合に使用します。スクリプトを実行する前に、スクリプトの内容からsha1チェックサムが生成され、関数が定義されているかどうか、関数テーブルで照会されます。

EVALSHAコマンドを使用するには、まずSCRIPT LOADコマンドを使用して関数をRedisにロードする必要があります。Redisは関数のsha1チェックサムを返し、それを使用してコマンドを直接実行することが可能です。

上記コマンドの使用例です。

127.0.0.1:6379> EVAL "return 'hello'" 0 0
"hello"

127.0.0.1:6379> SCRIPT LOAD "return redis.pcall('GET', ARGV[1])"
"20b602dcc1bb4ba8fca6b74ab364c05c58161a0a"

127.0.0.1:6379> EVALSHA 20b602dcc1bb4ba8fca6b74ab364c05c58161a0a 0 test
"zbs"

EVAL コマンドのプロトタイプは EVAL script numkeys key [key ...] arg [arg ...] KEYS[N] と {コード はキーと引数を参照していますが、KEYSとARGVの引数は両方とも数字の1から始まることに注意してください。

また、Luaスクリプトでは、RedisがNULLを返した場合、結果はnilではなくfalseになることに注意してください。

Lua スクリプトの例

以下は、構文を紹介するためのLuaスクリプトの例です(参考程度にご覧ください)。

RedisのhashSet AのフィールドBの値がCであり、RedisのキーCの値を取得する。

{コード

lpop 値が n になるか、リストが空になるまで、複数の値を一度に取り出します (パイプラインでも簡単にできます)。

ARGV[N] 

zsetの中で最もスコアの高いn個の要素に対応するハッシュセット内の詳細を取得する。

// use: EVAL script 2 A B

local tmpKey = redis.call('HGET', KEYS[1], KEYS[2]); 
return redis.call('GET', tmpKey); 

基本的な使い方はこのとおりですが、具体的なシナリオによって、より多くの応用が可能です。

いくつかの考え

実装の先には、いくつか考えるべきことがあります。

利用シーン

まずは、RedisにおけるLuaの利用シーンをまとめてみましょう。

  • Luaスクリプトを使用することで、アトミックな操作を実現し、異なるクライアントがRedisサーバにアクセスすることで生じるデータの競合を回避することができます。
  • {を使用します。 複数のリクエストの結果が互いに依存している場合、Luaスクリプトを使用して複数のリクエストを1つのリクエストに統合することができます。

ポイントに注意

Luaスクリプトを使用して、次のことにも注意する必要があります。

  • グローバル変数の使用はエラーとなり、Luaスクリプトの実行が停止しますが、安全のため、Luaスクリプトではグローバル変数を使用せず、変数定義時にローカルキーワードを追加してください。
  • Luaスクリプトの時間的複雑性に注意してください。Redisのシングルスレッドは、Luaスクリプトの実行もブロックします。
  • {を使用します。 Luaスクリプトでアトミックな処理を行う場合、Luaスクリプトがエラーを報告すると、直前のコマンドもロールバックできなくなることに注意してください。 {を使用します。 複数のRedisリクエストを一度に発行する場合はパイプラインを使用しますが、リクエストの前後に依存関係がないため、Luaスクリプトよりも利便性が高いです。

概要

{最近、仕事で大きな変化があったんだ。 最近の仕事は、ビジネスから技術スタックに大きな変化があった元から完全に異なっている、すべてのコードとビジネスは気持ちの自分のコントロールの外にある本当に不快です、仕事はすべて "検索エンジンを開き、構文はすべてチェックに頼る "と、毎日非常に遅くまで新しいものに慣れて、少し疲れて、本当に仕事はああ罪を見つけることです変更します。しかし、快適ゾーンのうち、充実感の後に、彼らは常に進歩していることを思い出させるだけでなく、達成感です。

新しいことに出会ったばかりで腑に落ちないし、「3日あればJavaをマスターできる」みたいな水物も書きたくないけど、仕事の後の時間は仕事に必要な技術スタックを補うために取られるし、面白いと思ったことを勉強する時間もないんだよね。

概要

上記はこの記事のすべての内容です、私はあなたの勉強や仕事のためのこの記事の内容は、特定の参照学習価値があることを願って、あなたが交換するメッセージを残すことができます質問がある場合は、BinaryDevelopのあなたのサポートに感謝します。