Redis
 Computer >> コンピューター >  >> プログラミング >> Redis

Lua:Redisユーザー向けガイド

Redisにはスクリプト言語が埋め込まれていると聞きましたが、まだitaを試していないのですか?これが、RedisサーバーでLuaのパワーを使用するために理解する必要があることのツアーです。

こんにちは、Lua!

最初のRedisLuaスクリプトは、意味のある方法で実際にRedisと対話することなく、値を返すだけです。

local msg = "Hello, world!"
return msg

これは簡単です。最初の行はourmessageを使用してローカル変数を設定し、2行目はその値をRedisサーバーからクライアントに返します。このファイルをhello.luaとしてローカルに保存します そしてそれをそのように実行します:

redis-cli --eval hello.lua

接続の問題?

このredis-cli この例では、Redisserverをローカルで実行していると想定しています。 RedisGreenなどのリモートサーバーを使用している場合は、ホストとポートの情報を指定する必要があります。 接続を見つける RedisGreenダッシュボードのボタンをクリックして、サーバーのログイン情報をすばやくコピーします。

参照:127.0.0.1:6379でRedisに接続できませんでした:接続が拒否されました。

これを実行すると、「Hello、world!」が出力されます。 EVALの最初の引数 完全なluaスクリプトです—ここではcatを使用しています ファイルからスクリプトを読み取るコマンド。 2番目の引数は、スクリプトがアクセスするRediskeyの数です。単純な「HelloWorld」スクリプトはキーにアクセスしないため、0を使用します 。

キーと引数へのアクセス

URL短縮サービスを構築しているとします。 URLが届くたびに、それを保存して、後でURLにアクセスするために使用できる一意の番号を返します。

Luaスクリプトを使用して、INCRを使用してRedisから一意のIDを取得します 一意のIDでキー設定されたハッシュにURLをすぐに保存します:

local link_id = redis.call("INCR", KEYS[1])
redis.call("HSET", KEYS[2], link_id, ARGV[1])
return link_id

call()を使用して、ここで初めてRedisにアクセスします function。call() の引数は、Redisに送信するコマンドです。最初にINCR <key> 次に、HSET <key> <field> <value> 。これらの2つのコマンドは順番に実行されます。このスクリプトの実行中、Redisは他に何も実行せず、非常に高速に実行されます。

2つのLuaテーブルKEYSにアクセスしています およびARGV 。テーブルは連想配列であり、データを構造化するためのLuaの唯一のメカニズムです。私たちの目的では、これらは最も使いやすい言語の配列に相当すると考えることができますが、この言語に不慣れな人々をつまずかせるこれら2つのLua-ismに注意してください。

  • テーブルは1ベースです。つまり、インデックス作成は1から始まります。したがって、mytableの最初の要素は mytable[1]です 、2番目はmytable[2] 、など。

  • テーブルはnil値を保持できません。操作によって[ 1, nil, 3, 4 ]のテーブルが生成される場合 、結果は代わりに[ 1 ]になります —テーブルは切り捨てられます 最初のnil値で。

このスクリプトを呼び出すときは、KEYSの値も渡す必要があります。 およびARGV テーブル。生のRedisプロトコルでは、コマンドは次のようになります。

EVAL $incrset.lua 2 links:counter links:url https://malcolmgladwellbookgenerator.com/

EVALを呼び出す場合 、スクリプトの後に2を提供します KEYSの数として アクセスされるので、KEYSをリストします 、最後にARGVの値を提供します 。

通常、Redis Luaスクリプトを使用してアプリをビルドする場合、Redisclientライブラリがキーの数の指定を処理します。上記のコードブロックは完全を期すために示されていますが、コマンドラインでこれを行う簡単な方法は次のとおりです。

redis-cli --eval incrset.lua links:counter links:urls , https://malcolmgladwellbookgenerator.com/

--evalを使用する場合 上記のように、コンマはKEYS[]を区切ります ARGV[]から アイテム。

わかりやすくするために、今回もKEYSを使用した元のスクリプトを示します。 およびARGV 拡張:

local link_id = redis.call("INCR", "links:counter")
redis.call("HSET", "links:urls", link_id, "https://malcolmgladwellbookgenerator.com")
return link_id

Redis用のLuaスクリプトを作成する場合、アクセスされるすべてのキーには、KEYSのみがアクセスする必要があります。 テーブル。 ARGV テーブルはパラメータの受け渡しに使用されます—ここでは保存したいURLの値です。

条件付きロジック:increxとhincrex

上記の例では、URL短縮サービスのリンクを保存していますが、URLにアクセスした回数も追跡する必要があります。そのために、Redisのハッシュにカウンターを保持します。ユーザーがリンク識別子を持ってきたら、それが存在するかどうかを確認し、存在する場合はそのカウンターをインクリメントします。

if redis.call("HEXISTS", KEYS[1], ARGV[1]) == 1 then
  return redis.call("HINCRBY", KEYS[1], ARGV[1], 1)
else
  return nil
end

誰かがショートリンクをクリックするたびに、このスクリプトを実行して、リンクが再び共有されたことを追跡します。 EVALを使用してスクリプトを呼び出します links:visitsを渡します 単一のキーと、前のスクリプトから単一の引数として返されたリンク識別子。

スクリプトはハッシュなしでほぼ​​同じように見えます。標準のRedisキーが存在する場合にのみインクリメントするスクリプトは次のとおりです。

if redis.call("EXISTS",KEYS[1]) == 1 then
  return redis.call("INCR",KEYS[1])
else
  return nil
end

SCRIPTLOADとEVALSHA

RedisがLuaスクリプトを実行しているときは、他には何も実行されないことに注意してください。最良のスクリプトは、必要最小限のロジックで、既存の小さなアトミックデータ操作のRedisボキャブラリーを拡張するだけです。 Luaスクリプトのバグにより、Redisサーバーが完全にロックされる可能性があります。短くしてデバッグしやすいようにするのが最善です。

通常は非常に短いですが、実行するたびに完全なLuaスクリプトを指定する必要はありません。実際のアプリケーションでは、代わりに、アプリケーションの起動時(またはデプロイ時)に各LuaスクリプトをRedisに登録し、後で一意のSHA-1識別子でスクリプトを呼び出します。

redis-cli SCRIPT LOAD "return 'hello world'"
# "5332031c6b470dc5a0dd9b4bf2030dea6d65de91"

redis-cli EVALSHA 5332031c6b470dc5a0dd9b4bf2030dea6d65de91 0
# "hello world"

SCRIPT LOADへの明示的な呼び出し EVALの場合、ライブアプリケーションでは通常不要です。 渡されるスクリプトを暗黙的にロードします。アプリケーションはEVALSHAを試みることができます 楽観的にEVALにフォールバックします スクリプトが見つからない場合のみ。

Rubyプログラマーの方は、ShopifyのWolverineをご覧ください。これにより、RubyアプリのLuaスクリプトの読み込みと保存が簡単になります。 PHPプログラマーの場合、Predisは、通常のRedisコマンドであるかのように呼び出されるLuascriptの追加をサポートしています。これらのツールやその他のツールを使用してLuaとのやり取りを標準化する場合は、お知らせください。他に何があるかを知りたいと思います。

いつLuaを使用しますか?

LuaのRedisサポートはWATCHと多少重複しています / MULTI / EXEC ブロック。操作をグループ化して、一緒に実行します。では、どのようにして一方を他方の上に使用することを選択しますか? MULTIの各操作 ブロックは独立している必要がありますが、Luaを使用すると、後の操作は前の操作の結果に依存する可能性があります。 Luaスクリプトを使用すると、WATCHのときに遅いクライアントを飢えさせる可能性のあるレース条件を回避することもできます。 使用されます。

RedisGreenで見たところ、Luaを使用するほとんどのアプリはMULTI / EXECも使用しますが、その逆はありません。最も成功しているLuaスクリプトは小さく、アプリに必要な1つの機能を実装するだけですが、Redisvocabularyの一部ではありません。

図書館への訪問

Redis Luaインタープリターは、base、table、string、math、debug、cjson、およびcmsgpackの7つのライブラリをロードします。最初のいくつかは、任意の言語に期待する基本的な操作を実行できる標準ライブラリです。最後の2つは、RedisにJSONとMessagePackを理解させます。これは非常に便利な機能であり、なぜこれほど頻繁に使用されないのか疑問に思っています。

パブリックAPIを備えたWebアプリでは、JSONがいたるところにある傾向があります。したがって、通常のRedisキーに多数のJSON BLOBが格納されていて、それらをハッシュとして格納したかのように、それらの内部の特定の値にアクセスしたい場合があります。RedisJSONのサポートにより、これは簡単です。

if redis.call("EXISTS", KEYS[1]) == 1 then
  local payload = redis.call("GET", KEYS[1])
  return cjson.decode(payload)[ARGV[1]]
else
  return nil
end

ここでは、キーが存在するかどうかを確認し、存在しない場合はすぐにnilを返します。次に、RedisからJSON値を取得し、cjson.decode()で解析します。 、要求された値を返します。

redis-cli set apple '{ "color": "red", "type": "fruit" }'
# OK

redis-cli --eval json-get.lua apple , type
# "fruit"

このスクリプトをRedisサーバーにロードすると、Redisに保存されているJSON値をハッシュであるかのように扱うことができます。オブジェクトが適度に小さい場合、アクセスごとに値を解析する必要がありますが、これは実際には非常に高速です。

パフォーマンスが要求されるシステムの内部APIで作業している場合は、JSONよりもMessagePackを選択する可能性が高くなります。これは、小さくて高速であるためです。幸いなことに、Redisでは(ほとんどの場所で)、MessagePackはほとんどドロップインプレースメントです。 JSONの場合:

if redis.call("EXISTS", KEYS[1]) == 1 then
  local payload = redis.call("GET", KEYS[1])
  return cmsgpack.unpack(payload)[ARGV[1]]
else
  return nil
end
数値の計算

LuaとRedisの型システムは異なるため、RedisとLuaの境界を越えると値がどのように変化するかを理解することが重要です。数値がLuaからRedisクライアントに戻ると、整数になります—小数点を超える数字はすべて削除されます:

local indiana_pi = 3.2
return indiana_pi

このスクリプトを実行すると、Redisは3の整数を返します—興味深いpiの断片を失います。簡単そうに見えますが、スクリプトの途中でRedisを操作し始めると、少し注意が必要になります。例:

local indiana_pi = 3.2
redis.call("SET", "pi", indiana_pi)
return redis.call("GET", "pi")

ここでの結果の値はastringです:"3.2" なんで? Redisには専用の数値タイプはありません。最初にSETしたとき 値、Redisはそれを文字列として保存し、Luaが最初に値をfloatと考えていたという事実のすべての記録を失います。後で値を引き出すとき、それはまだ文字列です。

GETでアクセスされるRedisの値 / SET INCRのような数値演算の場合を除いて、文字列と見なす必要があります およびDECR それらに対して実行されます。これらの特別な数値演算は実際には整数応答を返します(そして数学的な規則に従って格納された値を操作します)が、Redisに格納された値の「タイプ」は依然として文字列値です。

Gotchas:まとめ

これらは、RedisでLuaを操作するときに発生する最も一般的なエラーです:

  • ほとんどの一般的な言語とは異なり、テーブルはLuaをベースにしています。 KEYSテーブルの最初の要素はKEYS[1]です。 、2番目はKEYS[2] など

  • nil値は、Luaのテーブルを終了します。したがって、[ 1, 2, nil, 3 ] 自動的に[1, 2]になります 。テーブルでnil値を使用しないでください。

  • redis.call redis.pcallの間、例外スタイルのLuaエラーが発生します エラーを自動的にトラップし、検査可能なastablesを返します。

  • Lua番号は、Redisに送信されるときに整数に変換されます—小数点を超えるものはすべて失われます。浮動小数点数を文字列に変換してから返します。

  • Luaスクリプトで使用するすべてのキーをKEYSで必ず指定してください そうしないと、Redisの将来のバージョンでスクリプトが壊れてしまう可能性があります。

  • Luaスクリプトは、Redisの他の操作とまったく同じです。実行中は、他に何も実行されません。スクリプトは、Redisサーバーの語彙を拡張する方法と考えてください。短く、要領を得たものにしてください。

さらに読む

LuaとRedisのオンラインに関する優れたリソースはたくさんあります—ここにいくつかのIuseがあります:

  • EVALドキュメント
  • RedisGreenのLuaスクリプトライブラリ
  • Luaリファレンスマニュアル
  • Luaチュートリアルディレクトリ

  1. Android ユーザー向けの写真ガイド:Android 向けの 5 つの最高の写真アプリ

    あなたの Android は、あなたの日常活動のハブです。無数のタスクに使用します。仕事を終わらせたり、長年の友人とつながったりするのに最適な便利なデバイスです。スマートフォンで行うすべてのことから、写真も撮ります…そしてたくさんの写真を撮ります。しかし、あなたのショットはどのくらいの頻度で完璧ですか?ある調査によると、平均的なユーザーは 1 日に 322 枚の写真を撮り、別の調査によると、これらの写真に目を向ける時間はほとんどありません。 まあ!これはくだらないですね。 では、これらの写真を完璧で時代を超越したものにするにはどうすればよいでしょうか?さて、これら 5 つの最高の Androi

  2. ディスク クリーンアップ ガイド:Windows および Mac ユーザー向け

    「ディスク容量が不足しています」、「起動ディスクがほぼいっぱいです」 – ほとんどの人が、Windows または macOS プラットフォームでこれらの避けられないプロンプトに遭遇しました。ほこりやゴミが家の中の隠れた場所にたまるのと同じように、コンピューター マシンにも同じことが言えます。不要なファイル、プログラム、およびその他のジャンクのいくつかは時間の経過とともに蓄積され、最終的に速度が低下します。 そのため、コンピューターがハード ドライブがいっぱいであると通知している場合は、完全なディスク クリーンアップを実行できるようにお手伝いします。 しかし、まず、使用可能なディスク容量を