IRedisClient
redisサーバー側のLUAサポート用のAPIは、以下のよりユーザーフレンドリーなAPIにリファクタリングされています。
public interface IRedisClient
{
//Eval/Lua operations
T ExecCachedLua<T>(string scriptBody, Func<string, T> scriptSha1);
RedisText ExecLua(string body, params string[] args);
RedisText ExecLua(string luaBody, string[] keys, string[] args);
RedisText ExecLuaSha(string sha1, params string[] args);
RedisText ExecLuaSha(string sha1, string[] keys, string[] args);
string ExecLuaAsString(string luaBody, params string[] args);
string ExecLuaAsString(string luaBody, string[] keys, string[] args);
string ExecLuaShaAsString(string sha1, params string[] args);
string ExecLuaShaAsString(string sha1, string[] keys, string[] args);
int ExecLuaAsInt(string luaBody, params string[] args);
int ExecLuaAsInt(string luaBody, string[] keys, string[] args);
int ExecLuaShaAsInt(string sha1, params string[] args);
int ExecLuaShaAsInt(string sha1, string[] keys, string[] args);
List<string> ExecLuaAsList(string luaBody, params string[] args);
List<string> ExecLuaAsList(string luaBody, string[] keys, string[] args);
List<string> ExecLuaShaAsList(string sha1, params string[] args);
List<string> ExecLuaShaAsList(string sha1, string[] keys, string[] args);
string CalculateSha1(string luaBody);
bool HasLuaScript(string sha1Ref);
Dictionary<string, bool> WhichLuaScriptsExists(params string[] sha1Refs);
void RemoveAllLuaScripts();
void KillRunningLuaScript();
string LoadLuaScript(string body);
}
LUAでの効率的なSCAN#
以下のC#APIは、key:*
に一致する最初の10件の結果を返します。 パターン:
var keys = Redis.ScanAllKeys(pattern: "key:*", pageSize: 10)
.Take(10).ToList();
ただし、上記のC#ストリーミングAPIでは、リクエストを完了するために、不明な数のRedis操作(Redisのキーの数に制限されます)が必要です。より高いpageSize
を選択することで、SCAN呼び出しの数を減らすことができます。 SCAN操作が呼び出されるたびにさらに多くのキーをスキャンするようにRedisに指示します。
API呼び出しの数は、多数のRedis操作を引き起こす可能性があるため、複数の依存するリモートネットワーク呼び出しの遅延により、許容できない遅延が発生する可能性があります。簡単な解決策は、代わりに複数のSCAN呼び出しをRedisサーバーでインプロセスで実行し、複数のSCAN呼び出しのネットワーク遅延を排除することです。例:
const string FastScanScript = @"
local limit = tonumber(ARGV[2])
local pattern = ARGV[1]
local cursor = 0
local len = 0
local results = {}
repeat
local r = redis.call('scan', cursor, 'MATCH', pattern, 'COUNT', limit)
cursor = tonumber(r[1])
for k,v in ipairs(r[2]) do
table.insert(results, v)
len = len + 1
if len == limit then break end
end
until cursor == 0 or len == limit
return results";
RedisText r = redis.ExecLua(FastScanScript, "key:*", "10");
r.Children.Count.Print() //= 10
ExecLua
APIは、この複雑なLUAテーブルの応答をChildren
で返します RedisText
のコレクション 応答。
Alternative Complex API Response#
LUA操作で複雑なデータ構造を返す別の方法は、結果をJSONとしてシリアル化することです
return cjson.encode(results)
応答を次の文字列として解析することで、生のJSONとしてアクセスできます:
string json = redis.ExecLuaAsString(FastScanScript, "key:*", "10");
情報
これは、RedisReactのRedisServicesで使用されているアプローチでもあります。
ExecCachedLua#
ExecCachedLuaは便利な高レベルAPIであり、RDBMSストアドプロシージャが抱える問題の多くに悩まされている高性能サーバーLUAスクリプトの実行に必要な簿記を排除します。これは、RDBMSの既存の状態に依存します。ストアドプロシージャの最新バージョン。
Redis LUAを使用すると、呼び出されるたびにLUAスクリプト全体を送信、解析、ロードして実行するオプションがあります。または、スタートアップ時にLUAスクリプトをRedisにプリロードしてから、スクリプトのSHA1ハッシュを使用して実行することもできます。これに伴う問題は、Redisサーバーが誤ってフラッシュされた場合、存在しない既存のスクリプトに依存する壊れたアプリケーションが残ることです。新しいExecCachedLua
APIは、コンパイルされたSHA1スクリプトを常に実行し、帯域幅とCPUを節約するだけでなく、LUAスクリプトが存在しなくなった場合は再作成するという、両方の長所を提供します。
代わりに、上記のコンパイル済みLUAスクリプトをSHA1識別子で実行できます。この識別子は、存在しなかったか、実行時に削除されたかに関係なく機能し続けます。例:
// #1: Loads LUA script and caches SHA1 hash in Redis Client
r = redis.ExecCachedLua(FastScanScript, sha1 =>
redis.ExecLuaSha(sha1, "key:*", "10"));
// #2: Executes using cached SHA1 hash
r = redis.ExecCachedLua(FastScanScript, sha1 =>
redis.ExecLuaSha(sha1, "key:*", "10"));
// Deletes all existing compiled LUA scripts
redis.ScriptFlush();
// #3: Executes using cached SHA1 hash, gets NOSCRIPT Error,
// re-creates then re-executes the LUA script using its SHA1 hash
r = redis.ExecCachedLua(FastScanScript, sha1 =>
redis.ExecLuaSha(sha1, "key:*", "10"));
使用例#
ZPOPを実装する方法は次のとおりです ソートされたセットからランクが最も低いアイテムを削除するには、Luaで:
var luaBody = @"
local val = redis.call('zrange', KEYS[1], 0, ARGV[1]-1)
if val then redis.call('zremrangebyrank', KEYS[1], 0, ARGV[1]-1) end
return val";
var i = 0;
var alphabet = 26.Times(c => ((char)('A' + c)).ToString());
alphabet.ForEach(x => Redis.AddItemToSortedSet("zalphabet", x, i++));
//Remove the letters with the lowest rank from the sorted set 'zalphabet'
var letters = Redis.ExecLuaAsList(luaBody, keys: new[] { "zalphabet" }, args: new[] { "3" });
letters.PrintDump(); //[A, B, C]
そして、 ZREVPOPを実装する方法 並べ替えられたセットから最高ランクのアイテムを削除するには:
var luaBody = @"
local val = redis.call('zrange', KEYS[1], -ARGV[1], -1)
if val then redis.call('zremrangebyrank', KEYS[1], -ARGV[1], -1) end
return val";
var i = 0;
var alphabet = 26.Times(c => ((char)('A' + c)).ToString());
alphabet.ForEach(x => Redis.AddItemToSortedSet("zalphabet", x, i++));
//Remove the letters with the highest rank from the sorted set 'zalphabet'
List<string> letters = Redis.ExecLuaAsList(luaBody,
keys: new[] { "zalphabet" }, args: new[] { "3" });
letters.PrintDump(); //[X, Y, Z]
その他の例#
int
を返す :
int intVal = Redis.ExecLuaAsInt("return 123"); //123
int intVal = Redis.ExecLuaAsInt("return ARGV[1] + ARGV[2]", "10", "20"); //30
string
を返す :
//Hello, Redis Lua!
var strVal = Redis.ExecLuaAsString(@"return 'Hello, ' .. ARGV[1] .. '!'", "Redis Lua");
List
を返す 文字列の数:
Enum.GetNames(typeof(DayOfWeek)).ToList()
.ForEach(x => Redis.AddItemToList("DaysOfWeek", x));
var daysOfWeek = Redis.ExecLuaAsList("return redis.call('LRANGE', 'DaysOfWeek', 0, -1)");
daysOfWeek.PrintDump(); //[Sunday, Monday, Tuesday, ...]
その他の例は、RedisEvalLuaテストで見つけることができます