Redisのストアドプロシージャ

画像

多くの人は、SQLデータベースにプロシージャを保存する機能について知っています。これについては、たくさんのふさふさしたマニュアルや記事が書かれています。 ただし、Redisにバージョン2.6.0以降同様の機能があることを知っている人はほとんどいません。 しかし、Redisはリレーショナルデータベースではないため、ストアドプロシージャを記述するための原則はまったく異なります。 Redisのストアドプロシージャはほぼ完全なLuaスクリプトです(執筆時点では、Lua 5.1はインタープリターとして使用されています)。

さらに物語は、Redis APIの基本的な紹介と、 redis-serverプロセスがlocalhost:6379で実行されていることを前提としています 。 Redisを初めて使用する場合は、次の資料を読む前に、 Redisとは何かについての簡単な情報をお読みください。 また、少なくとも部分的には、 このインタラクティブガイドをご覧ください

こんにちは世界!


redis-cliを使用して、データベースから文字列「Hello world!」を返します。
redis-cli EVAL 'return "Hello world!"' 0 

結果:
 "Hello world!" 

何が起こったのか見てみましょう:
  1. 2つの引数を持つRedis組み込みEVALコマンドの呼び出し。 最初に
     return "Hello world!" 
    -Lua関数の本体。
     0 
    -関数のパラメーターとして渡されるRedisキーの数。 Redisキーをパラメーターとして渡すまで、つまり 0を指定します。
  2. サーバー上のプログラムテキストを解釈し、Lua文字列値を返す
  3. Lua文字列をredis一括返信に変換する
  4. redis-cliで結果を取得する
  5. redis-cliはstdoutへの一括返信を出力します


Redisのストアドプロシージャは通常のLua関数であるため、引数を受け取って返す原理は似ています。
注: Luaは、mul-return(関数から複数の結果を返す)をサポートしています。 ただし、redisから複数の値を返すには、マルチバルク応答を使用する必要があり、Luaのテーブルが表示されているため、以下の例は期待どおりに機能しません。
 redis-cli EVAL 'return "Hello world!", "test"' 0 

 "Hello world!" 

結果は1つの戻り値(最初)に切り捨てられます。

こんにちは%username%


先に進みます。 引数のない関数は特に重要ではないため、引数の処理を関数に追加します。
ドキュメントによると、EVALを介して実行される関数は、LuaテーブルKEYSおよびARGVを介して任意の数の引数を受け入れることができます。 名前を含む文字列が引数として渡される場合、これを使用して%username%に挨拶します。そうでない場合は、Habrに挨拶します。

引数なしで呼び出します。LuaのARGVテーブル配列は空です。つまり、ARGV [1]はnilを返します。
 redis-cli EVAL 'return "Hello " .. (ARGV[1] or "Habr") .. "!"' 0 

結果:
 "Hello Habr!" 

そして、文字列「Innocent」をパラメーターとして渡します。
 redis-cli EVAL 'return "Hello " .. (ARGV[1] or "Habr") .. "!"' 0 '' 

結果:
 "Hello \xd0\x98\xd0\xbd\xd0\xbd\xd0\xbe\xd0\xba\xd0\xb5\xd0\xbd\xd1\x82\xd0\xb8\xd0\xb9!" 

注: Redisは文字列をutf8に保存し、redis-cliのクライアント側の問題を回避するために、asciiにない文字はエスケープシーケンスとして表示されます。 bashで読み取り可能な文字列を表示するには、次のようにします。
 echo -e $(redis-cli EVAL 'return "Hello " .. ARGV[1] .. "!"' 0 '') 


スクリプトからのRedis APIアクセス


各Luaスクリプトで、インタープリターはこれらのライブラリーをロードします。
 string, math, table, debug, cjson, cmsgpack 

最初の4つはLuaの標準です。 最後の2つはそれぞれjsonとmsgpackを操作するためのものです。

リポジトリ内のデータを操作するために、「redis」モジュールがLuaにエクスポートされました。 このモジュールのcall関数を使用して、 redis-cliからのコマンドに対応する形式でコマンドを実行できます。

データベースにユーザーが存在するかどうかを確認し、存在する場合は、一致するログイン/パスワードのペアを確認するサンプルスクリプトでredis.callを使用することを検討してください。

データベースに、ログインとパスワードのペアを含むテストデータセットを作成します。
 redis-cli HMSET 'users' 'ivan' '12345' 'maria' 'qwerty' 'oleg' '1970-01-01' 

 OK 


すべてが本当に問題ないことを確認してください。
 redis-cli HGETALL 'users' 

 1) "ivan" 2) "12345" 3) "maria" 4) "qwerty" 5) "oleg" 6) "1970-01-01" 


スクリプト入力に1つの引数、json文字列を次の形式で指定します。
 { "login":"userlogin", "password":"userpassword" } 


ユーザーが存在し、jsonのパスワードがデータベースのパスワードと一致する場合、スクリプトは1を返します。それ以外の場合は0です。たとえば、入力形式が正しくない場合、引数はスクリプトに渡されなかった(ARGV [1] == nil )、またはjsonで必須フィールドの1つが欠落しています、エラー情報を含む読み取り可能な文字列を返します。

解析とパッケージ化のために、json redisはcjsonモジュールをLuaにエクスポートします。 スクリプトでは、このモジュールのデコード機能を使用します。 パラメーターとして、関数はjsonを含むLua-stringを取り、戻り値はLua-tableで、そのストリングキーはjson-fieldsです。

次の内容でlogin.luaファイルを作成します。
Login.luaスクリプトコード
 local jsonPayload = ARGV[1] if not jsonPayload then return 'No such json data' end local user = cjson.decode(jsonPayload) if not user.login then return 'User login is not set' end if not user.password then return 'User password is not set' end --  redis API  Lua   API redis. local expectedPassword = redis.call('HGET', 'users', user.login) if not expectedPassword then return 0 end if expectedPassword ~= user.password then return 0 end return 1 



使用例:
  1. パスワードが一致
     redis-cli EVAL "$(cat login.lua)" 0 '{"login":"maria","password":"qwerty"}' 

     (integer) 1 

  2. パスワードが一致しません
     redis-cli EVAL "$(cat login.lua)" 0 '{"login":"maria","password":"12345"}' 

     (integer) 0 

  3. JSONにはパスワードフィールドがありません
     redis-cli EVAL "$(cat login.lua)" 0 '{"login":"maria","pwd":"12345"}' 

     "User password is not set" 

  4. JSONを含む引数が渡されませんでした
     redis-cli EVAL "$(cat login.lua)" 0 

     "No such json data" 



注: Redisのすべてのキーは、SETおよびGETを介して操作するだけでなく、文字列表現を持ちます。 Redisには整数型はなく、フロートもありません。 これを理解することが重要です。 次の例では、テストキーの値を文字列として返します。
 redis-cli SET test 5 

 OK 

保存された値のタイプを調べます。
 redis-cli TYPE test 

 string 

戻りますが、スクリプトを通じて:
 redis-cli EVAL "return redis.call('GET', 'test')" 0 

 "5" 


同時に、整数を返すことを誰も禁止していません(整数一括返信として):
 redis-cli EVAL "return tonumber(redis.call('GET', 'test'))" 0 

 (integer) 5 


Luis番号をredis.call関数のパラメーターとして渡すことに注意してください。
 redis-cli EVAL "return redis.call('SET', 'test', 5.6)" 0 

 OK 

値はより小さい整数に切り捨てられます。
 redis-cli EVAL "return tonumber(redis.call('GET', 'test'))" 0 

 (integer) 5 

しかし、実際に内部にあるのは:
 redis-cli GET test 

 "5.5999999999999996" 

「正しい」方法:
 redis-cli EVAL "return redis.call('SET', 'test', tostring(5.6))" 0 

 OK 

 redis-cli GET test 

 "5.6" 


どうやら、Lua番号変換はLuaインタープリターではなく、Cで書かれたRedisのネイティブ部分で発生します。

今日は以上です。

こちらもご覧ください


redis.io/commands/eval
www.redisgreen.net/blog/intro-to-lua-for-redis-programmers
redislabs.com/blog/5-methods-for-tracing-and-debugging-redis-lua-scripts

Source: https://habr.com/ru/post/J270251/


All Articles