.Net Coreは分散キャッシュRedisを使用します.Luaスクリプト
15983 ワード
はじめに
実行環境window、redisバージョン3.2.1.ここでは一旦Luaを除いて詳しく説明します.Redisの方面だけから説明します.
二、RedisのLuaスクリプト
Redisの2.6バージョンでスクリプト機能が導入され、開発者がLua言語でスクリプトを作成してRedisに実行することができます.LuaスクリプトではほとんどのRedisコマンドを呼び出すこともできます.スクリプトを使うと次の3つの利点があります.
(1)ネットワークオーバーヘッドを低減する:処理データの取得を複数回要求する場合があるが、スクリプト機能を使用すると、1回の要求だけで同じ操作が完了し、ネットワークの往復遅延が低減される.
(2)原子操作:Redisはシナリオ全体を一つの全体として実行し、中間は他のコマンドに挿入されない.つまり脚本を書く過程で競合条件が心配される必要はなく、事務を使う必要がないということです.仕事ができる機能はすべてシナリオでできます.
(3)多重化:クライアントから送信されたスクリプトは、Redisに永久に記憶され、これは他のクライアント(他の言語で開発されたプロジェクトであってもよい)がコードを使用して同じ論理を完了する必要がなく、このスクリプトを多重化することができることを意味する.
三、Redis呼び出しLua
1、EVAL命令
スクリプトを作成した後、最も重要なのはプログラムでスクリプトを実行することです.Redisは、EVALコマンドを提供し、開発者が他のRedis内蔵コマンドを呼び出すようにスクリプトを呼び出すことができます.EVALのコマンドフォーマットは以下の通りです.
上に直接luaスクリプトを書く以外に、luaスクリプトファイルを読み込んでスクリプトを実行することができます.コマンドは以下の通りです.
シナリオが長い場合を考慮して、シナリオを呼び出すたびに全体のスクリプトをRedisに渡す必要がある場合は、より多くの帯域幅を占めます.したがって、Redisは、EVALSHAコマンドを提供し、開発者がシナリオ内容のSHA 1ダイジェストを通じてスクリプトを実行することができます.このコマンドの使い方はEVALと同じですが、シナリオ内容のscriptをSHA 1ダイジェストに置き換えます.
Redisは、EVALコマンドを実行すると、スクリプトのSHA 1ダイジェストを計算してスクリプトキャッシュに記録しますが、EVALSHAコマンドを実行するときは、スクリプトキャッシュから該当するダイジェストが見つからないとエラーを返します.
(1)まず脚本SHA 1のダイジェストを計算し、EVALSHAを用いて実行する.
(2)戻り値を取得し、エラーが戻ったらEVALを使ってスクリプトを再実行します.
3、SCRIPT LOAD命令
スクリプトをスクリプトキャッシュに追加したいだけで実行しない場合は、SCRIPT_LOADコマンドを使用して、値を返した場合はスクリプトのSHA 1ダイジェストを返します.
SCRIPT EXISTSコマンドは、1つまたは複数のスクリプトのSHA 1ダイジェストがキャッシュされているかどうかを同時に検索できます.1はゼロが存在していますか?
RedisはシナリオのSHA 1ダイジェストをスクリプトキャッシュに追加して永久に保存します.削除はしませんが、SCRIPT FLUSHですべてのスクリプトキャッシュを削除することができます.
Redisのシナリオは原子的であるため、スクリプト実行中に他のコマンドは実行されない.あるスクリプトの実行時間が長すぎて、Redisがサービスを提供できなくなることを防ぐために、Redisはlua-time-limitパラメータ制限シナリオの最長動作時間を提供しています.デフォルトは5秒です.再スクリプト実行中に他のコマンドを実行すると「BUSY」エラーに戻ります.
SCRIPT KILLにより、現在のスクリプトの実行が終了され、スクリプトが停止し、エラーが返されます.
四、Redisはスクリプトの戻り値を取得する.
多くの場合、スクリプトはリセットで値を返す必要がありますが、実行されていない場合はデフォルトではnilに戻ります.私たちは他のRedis内蔵コマンドを呼び出すように自分たちが書いたスクリプトを呼び出すことができますので、Redisは自動的にスクリプトの戻り値のLuaデータタイプをRedisの戻り値のタイプに変換します.具体的な変換ルールは以下の通りです.
(1)Luaの数字タイプで、Redisは整数タイプです.
(3)Luaの表タイプ(配列形式)は、Redisは複数行の文字列を返します.
Redisスクリプトは、Lua標準ライブラリにおけるファイルまたはシステム呼び出しに関する関数の使用を禁止しており、シナリオではRedisのデータのみを処理することができます.また、Redisは、スクリプトのグローバル変数を無効にすることによって、各スクリプトが相対的に分離され、互いに干渉しないことを保証しています.
サーバのセキュリティを保証するだけでなく、シナリオの実行結果はシナリオ自体と実行時に伝達されるパラメータのみに関係し、外部条件(システム時間、システム内のファイルの内容、その他のスクリプト実行結果登)に依存しないことを確認しました.これは、シナリオ呼び出しのコマンドではなく、コピーとAOFの永続的な動作を行うときに記録されるスクリプトの内容であるため、スクリプトの内容とパラメータが同じであることを前提にスクリプトの実行結果は同じであることが保証されます.
乱数については、Redisがmath.randomとmathを入れ替えた.ランドmseed関数は、スクリプトを実行するたびに生成される乱数列を同じにします.乱数シーケンスが異なる場合、最も簡単な方法では、プログラムによって乱数を生成してスクリプトにパラメータを渡すか、あるいはより柔軟な方法でプログラム内で乱数を生成してスクリプトにランダムシードとして送信します.
六、ネットコールでシナリオを使う
とても簡単で、直接コードを書いて、ここで一番基本的な例を挙げます.一番簡単にevalを使います.
実行環境window、redisバージョン3.2.1.ここでは一旦Luaを除いて詳しく説明します.Redisの方面だけから説明します.
二、RedisのLuaスクリプト
Redisの2.6バージョンでスクリプト機能が導入され、開発者がLua言語でスクリプトを作成してRedisに実行することができます.LuaスクリプトではほとんどのRedisコマンドを呼び出すこともできます.スクリプトを使うと次の3つの利点があります.
(1)ネットワークオーバーヘッドを低減する:処理データの取得を複数回要求する場合があるが、スクリプト機能を使用すると、1回の要求だけで同じ操作が完了し、ネットワークの往復遅延が低減される.
(2)原子操作:Redisはシナリオ全体を一つの全体として実行し、中間は他のコマンドに挿入されない.つまり脚本を書く過程で競合条件が心配される必要はなく、事務を使う必要がないということです.仕事ができる機能はすべてシナリオでできます.
(3)多重化:クライアントから送信されたスクリプトは、Redisに永久に記憶され、これは他のクライアント(他の言語で開発されたプロジェクトであってもよい)がコードを使用して同じ論理を完了する必要がなく、このスクリプトを多重化することができることを意味する.
三、Redis呼び出しLua
1、EVAL命令
スクリプトを作成した後、最も重要なのはプログラムでスクリプトを実行することです.Redisは、EVALコマンドを提供し、開発者が他のRedis内蔵コマンドを呼び出すようにスクリプトを呼び出すことができます.EVALのコマンドフォーマットは以下の通りです.
127.0.0.1:6379> eval script numkeys key [key ...] arg [arg ...]
スクリプト内容.numkeys:keyパラメータの数.keyとarg:これらの2つのパラメータはシナリオにデータを伝達し、それらの値はKEYS[index]とARGV[index]の2つのテーブルタイプのグローバル変数を使用して、numkeysはkeyの数とそのインデックスの最大値であり、argvのインデックスはkeyとargvの合計からnumkeysを減算し、それらのインデックスはすべて1からnilに戻ります.以下のとおりですC:\Users\Xu>redis-cli
127.0.0.1:6379> eval 'return ARGV[3]' 2 key1 key2 value1 value2 value3
"value3"
127.0.0.1:6379> eval 'return KEYS[2]' 2 key1 key2 value1 value2 value3
"key2"
127.0.0.1:6379> eval 'return KEYS[3]' 2 key1 key2 value1 value2 value3
(nil)
その中で書くキーの名前はkeyパラメータで、他のデータは全部argパラメータです.上に直接luaスクリプトを書く以外に、luaスクリプトファイルを読み込んでスクリプトを実行することができます.コマンドは以下の通りです.
C:\Users\Xu>redis-cli --eval lua_file_path key1 key2 , arg1 arg2 arg3
numkeysは必要ありません.コンマの前後にスペースが必要です.結合された文字列とみなされます.//lua
return ARGV[2]
//
C:\Users\Xu>redis-cli.exe --eval e:\redis\a.lua key1 , value1 value2
"value2"
C:\Users\Xu>redis-cli.exe --eval e:\redis\a.lua key1 , value1 value2,value3
"value2,value3"
2、EVALSHA命令シナリオが長い場合を考慮して、シナリオを呼び出すたびに全体のスクリプトをRedisに渡す必要がある場合は、より多くの帯域幅を占めます.したがって、Redisは、EVALSHAコマンドを提供し、開発者がシナリオ内容のSHA 1ダイジェストを通じてスクリプトを実行することができます.このコマンドの使い方はEVALと同じですが、シナリオ内容のscriptをSHA 1ダイジェストに置き換えます.
Redisは、EVALコマンドを実行すると、スクリプトのSHA 1ダイジェストを計算してスクリプトキャッシュに記録しますが、EVALSHAコマンドを実行するときは、スクリプトキャッシュから該当するダイジェストが見つからないとエラーを返します.
127.0.0.1:6379> evalsha c349a436bd639369c62c971941fc5f7a80626ee6 1 key1 value1
(integer) 666
127.0.0.1:6379> evalsha c349a436bd639369c62c971941fc5f7a80626ee61 1 key1 value1
(error) NOSCRIPT No matching script. Please use EVAL.
プログラムでEVALSHAを使う流れは以下の通りです.(1)まず脚本SHA 1のダイジェストを計算し、EVALSHAを用いて実行する.
(2)戻り値を取得し、エラーが戻ったらEVALを使ってスクリプトを再実行します.
3、SCRIPT LOAD命令
スクリプトをスクリプトキャッシュに追加したいだけで実行しない場合は、SCRIPT_LOADコマンドを使用して、値を返した場合はスクリプトのSHA 1ダイジェストを返します.
127.0.0.1:6379> script load 'return 666'
"c349a436bd639369c62c971941fc5f7a80626ee6"
4、SCRIPT EXISTS命令SCRIPT EXISTSコマンドは、1つまたは複数のスクリプトのSHA 1ダイジェストがキャッシュされているかどうかを同時に検索できます.1はゼロが存在していますか?
127.0.0.1:6379> script exists c349a436bd639369c62c971941fc5f7a80626ee6 123ls436bd639369c62c971941fc5f7a80626ee6
1) (integer) 1
2) (integer) 0
5、SCRIPT FLUSHコマンドRedisはシナリオのSHA 1ダイジェストをスクリプトキャッシュに追加して永久に保存します.削除はしませんが、SCRIPT FLUSHですべてのスクリプトキャッシュを削除することができます.
127.0.0.1:6379> script flush
OK
(1.51s)
6、SCRIPT KILLとSHUTDOWN NOSAVERedisのシナリオは原子的であるため、スクリプト実行中に他のコマンドは実行されない.あるスクリプトの実行時間が長すぎて、Redisがサービスを提供できなくなることを防ぐために、Redisはlua-time-limitパラメータ制限シナリオの最長動作時間を提供しています.デフォルトは5秒です.再スクリプト実行中に他のコマンドを実行すると「BUSY」エラーに戻ります.
(A)127.0.0.1:6379> eval 'while true do end' 0
(B)127.0.0.1:6379> get foo
(error) BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE.
このときRedisは二つのコマンドしか受け付けられません.SCRIPT KILLとSHUTDOWN NOSVAEです.SCRIPT KILLにより、現在のスクリプトの実行が終了され、スクリプトが停止し、エラーが返されます.
(B)127.0.0.1:6379> script kill
OK
(B)127.0.0.1:6379> get foo
(nil)
(A)127.0.0.1:6379> eval 'while true do end' 0
(error) ERR Error running script (call to f_694a5fe1ddb97a4c6a1bf299d9537c7d3d0f84e7): @user_script:1: Script killed by user with SCRIPT KILL...
(175.99s)
現在実行されているスクリプトがRedisのデータを修正した場合、SCRIPT_KILLはシナリオの動作を終了しません.これは、SHUTDOWN NOSAVEを通じてRedisを強制的に終了させ、元のスクリプトの修正操作を元に戻す必要があります.持続的な操作を行わないということは、前回のスナップショットの後に送信されたすべてのデータベース修正が失われることを意味します.四、Redisはスクリプトの戻り値を取得する.
多くの場合、スクリプトはリセットで値を返す必要がありますが、実行されていない場合はデフォルトではnilに戻ります.私たちは他のRedis内蔵コマンドを呼び出すように自分たちが書いたスクリプトを呼び出すことができますので、Redisは自動的にスクリプトの戻り値のLuaデータタイプをRedisの戻り値のタイプに変換します.具体的な変換ルールは以下の通りです.
(1)Luaの数字タイプで、Redisは整数タイプです.
127.0.0.1:6379> eval 'return 1.1' 0
(integer) 1
(2)Luaの文字列タイプ、Redisも文字列タイプです.(3)Luaの表タイプ(配列形式)は、Redisは複数行の文字列を返します.
127.0.0.1:6379> eval 'return {0,1}' 0
1) (integer) 0
2) (integer) 1
(4)Luaテーブルタイプ(一つのokフィールドだけが状態情報を格納しています.)Redisは成功状態の回復です.127.0.0.1:6379> eval 'return {ok="this is ok"}' 0
this is ok
(5)Luaテーブルタイプ(一つのerrフィールドだけが状態情報を格納しています.)Redisはエラー状態応答です.127.0.0.1:6379> eval 'return {err="so bad"}' 0
(error) so bad
(6)Luaのbookタイプの中でtrueはRedisの1であり、falseはnilである127.0.0.1:6379> eval 'return true' 0
(integer) 1
127.0.0.1:6379> eval 'return false' 0
(nil)
五、サンドボックスと乱数Redisスクリプトは、Lua標準ライブラリにおけるファイルまたはシステム呼び出しに関する関数の使用を禁止しており、シナリオではRedisのデータのみを処理することができます.また、Redisは、スクリプトのグローバル変数を無効にすることによって、各スクリプトが相対的に分離され、互いに干渉しないことを保証しています.
サーバのセキュリティを保証するだけでなく、シナリオの実行結果はシナリオ自体と実行時に伝達されるパラメータのみに関係し、外部条件(システム時間、システム内のファイルの内容、その他のスクリプト実行結果登)に依存しないことを確認しました.これは、シナリオ呼び出しのコマンドではなく、コピーとAOFの永続的な動作を行うときに記録されるスクリプトの内容であるため、スクリプトの内容とパラメータが同じであることを前提にスクリプトの実行結果は同じであることが保証されます.
乱数については、Redisがmath.randomとmathを入れ替えた.ランドmseed関数は、スクリプトを実行するたびに生成される乱数列を同じにします.乱数シーケンスが異なる場合、最も簡単な方法では、プログラムによって乱数を生成してスクリプトにパラメータを渡すか、あるいはより柔軟な方法でプログラム内で乱数を生成してスクリプトにランダムシードとして送信します.
六、ネットコールでシナリオを使う
とても簡単で、直接コードを書いて、ここで一番基本的な例を挙げます.一番簡単にevalを使います.
var script = " return KEYS[1];";
var keys = new RedisKey[]{ "key1","key2"};
var values = new RedisValue[] { "value1", "value2" };
return await redisConnection.GetDatabase().ScriptEvaluateAsync(script, keys, values);
スクリプトをキャッシュして使用します.var bytes = await redisConnection.GetServer(Config.Get("ConnectionStrings:Redis:ConnectionString")).ScriptLoadAsync("return 1");
var result = await redisConnection.GetDatabase().ScriptEvaluateAsync(bytes, null, null);
スクリプトはキャッシュされていますか?bool exist = await redisConnection.GetServer(Config.Get("ConnectionStrings:Redis:ConnectionString")).ScriptExistsAsync("return 1");
すべてのスクリプトキャッシュを削除します.この操作は接続する必要があるConfigrationOptionsの設定の中でAllowAdmin=trueで、エラーがありません.redisConnection.GetServer(Config.Get("ConnectionStrings:Redis:ConnectionString")).ScriptFlush();
また、LuabScriptとLoadedcure Scriptの2つのクラスは、スクリプトに対してより複雑なスクリプトを行うことができます.LuaScriptは@myVar形式のスクリプトの変数をredisに必要な適切なARGV[someIndex]に書き換えます.伝達パラメータがRedisKeyタイプの場合、KEYSセットの一部として自動送信されます.次のとおりですvar lua = LuaScript.Prepare("return @key");
var result = redisConnection.GetDatabase().ScriptEvaluate(lua,new {key= (RedisKey)"key1",value = "value1" });