Redis
 sql >> Datenbank >  >> NoSQL >> Redis

RedisClient LUA-APIs

Der IRedisClient APIs für die serverseitige Redis-LUA-Unterstützung wurden in die benutzerfreundlicheren APIs unten umgestaltet:

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);
}

Effizienter SCAN in LUA #

Die folgende C#-API gibt die ersten 10 Ergebnisse zurück, die mit key:* übereinstimmen Muster:

var keys = Redis.ScanAllKeys(pattern: "key:*", pageSize: 10)
    .Take(10).ToList();

Die obige C#-Streaming-API erfordert jedoch eine unbekannte Anzahl von Redis-Vorgängen (begrenzt durch die Anzahl der Schlüssel in Redis), um die Anforderung abzuschließen. Durch die Wahl einer höheren pageSize kann die Anzahl der SCAN-Aufrufe reduziert werden um Redis anzuweisen, bei jedem Aufruf der SCAN-Operation weitere Schlüssel zu scannen.

Da die Anzahl der API-Aufrufe das Potenzial hat, zu einer großen Anzahl von Redis-Vorgängen zu führen, kann dies aufgrund der Latenz mehrerer abhängiger Remote-Netzwerkaufrufe zu einer inakzeptablen Verzögerung führen. Eine einfache Lösung besteht darin, die mehreren SCAN-Aufrufe stattdessen prozessintern auf dem Redis-Server ausführen zu lassen, wodurch die Netzwerklatenz mehrerer SCAN-Aufrufe eliminiert wird, z. B.:

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

Die ExecLua Die API gibt diese komplexe LUA-Tabellenantwort in den Children zurück Sammlung des RedisText Antwort.

Alternative komplexe API-Antwort #

Eine andere Möglichkeit, komplexe Datenstrukturen in einer LUA-Operation zurückzugeben, besteht darin, das Ergebnis als JSON

zu serialisieren
return cjson.encode(results)

Auf die Sie als unformatiertes JSON zugreifen können, indem Sie die Antwort als String parsen mit:

string json = redis.ExecLuaAsString(FastScanScript, "key:*", "10");

INFO

Dies ist auch der Ansatz, der in den RedisServices von Redis React verwendet wird.

ExecCachedLua #

ExecCachedLua ist eine praktische High-Level-API, die die Buchhaltung eliminiert, die für die Ausführung von Hochleistungs-Server-LUA-Skripten erforderlich ist, die unter vielen der Probleme leiden, die gespeicherte RDBMS-Prozeduren haben, die vom bereits vorhandenen Zustand im RDBMS abhängen, der mit aktualisiert werden muss neueste Version der gespeicherten Prozedur.

Mit Redis LUA haben Sie entweder die Möglichkeit, das gesamte LUA-Skript bei jedem Aufruf zu senden, zu parsen, zu laden und dann auszuführen, oder alternativ können Sie das LUA-Skript einmal beim Start in Redis vorladen und es dann mit dem SHA1-Hash des Skripts ausführen. Das Problem dabei ist, dass Sie, wenn der Redis-Server versehentlich geleert wird, mit einer kaputten Anwendung zurückbleiben, die sich auf ein bereits vorhandenes Skript stützt, das nicht mehr vorhanden ist. Das neue ExecCachedLua Die API bietet das Beste aus beiden Welten, indem sie immer das kompilierte SHA1-Skript ausführt, Bandbreite und CPU spart, aber auch das LUA-Skript neu erstellt, wenn es nicht mehr existiert.

Sie können stattdessen das oben kompilierte LUA-Skript mit seiner SHA1-Kennung ausführen, die weiterhin funktioniert, unabhängig davon, ob sie nie existierte oder zur Laufzeit entfernt wurde, z. B.:

// #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"));

Nutzungsbeispiele #

So können Sie ein ZPOP implementieren in Lua, um die Elemente mit dem niedrigsten Rang aus einer sortierten Menge zu entfernen:

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]

Und wie man ZREVPOP implementiert So entfernen Sie Elemente mit dem höchsten Rang aus einem sortierten Satz:

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]

Andere Beispiele #

Rückgabe eines int :

int intVal = Redis.ExecLuaAsInt("return 123"); //123
int intVal = Redis.ExecLuaAsInt("return ARGV[1] + ARGV[2]", "10", "20"); //30

Zurückgeben einer string :

//Hello, Redis Lua!
var strVal = Redis.ExecLuaAsString(@"return 'Hello, ' .. ARGV[1] .. '!'", "Redis Lua");

Zurückgeben einer List von Strings:

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, ...]

Weitere Beispiele finden Sie in den Redis Eval Lua-Tests