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



All Articles