Redis
 sql >> Database >  >> NoSQL >> Redis

API LUA RedisClient

Il IRedisClient Le API per il supporto LUA lato server Redis sono state riformulate nelle API più intuitive di seguito:

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

SCANSIONE efficiente in LUA #

L'API C# di seguito restituisce i primi 10 risultati corrispondenti alla key:* modello:

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

Tuttavia, l'API di streaming C# sopra richiede un numero sconosciuto di operazioni Redis (limitato al numero di chiavi in ​​Redis) per completare la richiesta. Il numero di chiamate SCAN può essere ridotto scegliendo un pageSize più alto per dire a Redis di scansionare più chiavi ogni volta che viene chiamata l'operazione SCAN.

Poiché il numero di chiamate API può potenzialmente comportare un numero elevato di operazioni Redis, può finire per produrre un ritardo inaccettabile a causa della latenza di più chiamate di rete remote dipendenti. Una soluzione semplice consiste invece nell'eseguire più chiamate SCAN in corso sul server Redis, eliminando la latenza di rete di più chiamate SCAN, ad esempio:

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

Il ExecLua L'API restituisce questa complessa risposta della tabella LUA in Children raccolta del RedisText Risposta.

Risposta API complessa alternativa #

Un altro modo per restituire strutture di dati complesse in un'operazione LUA consiste nel serializzare il risultato come JSON

return cjson.encode(results)

A cui puoi accedere come JSON grezzo analizzando la risposta come una stringa con:

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

INFO

Questo è anche l'approccio utilizzato nei RedisServices di Redis React.

ExecCachedLua #

ExecCachedLua è una comoda API di alto livello che elimina la contabilità richiesta per l'esecuzione di script LUA del server ad alte prestazioni che soffrono di molti dei problemi che hanno le stored procedure RDBMS che dipendono dallo stato preesistente nell'RDBMS che deve essere aggiornato con il ultima versione della Stored Procedure.

Con Redis LUA hai la possibilità di inviare, analizzare, caricare, quindi eseguire l'intero script LUA ogni volta che viene chiamato o in alternativa puoi precaricare lo script LUA in Redis una volta all'avvio e quindi eseguirlo utilizzando l'hash SHA1 dello script. Il problema con questo è che se il server Redis viene svuotato accidentalmente, rimani con un'applicazione rotta che si basa su uno script preesistente che non è più lì. Il nuovo ExecCachedLua L'API offre il meglio di entrambi i mondi in cui eseguirà sempre lo script SHA1 compilato, risparmiando larghezza di banda e CPU, ma ricreerà anche lo script LUA se non esiste più.

Puoi invece eseguire lo script LUA compilato sopra tramite il suo identificatore SHA1, che continua a funzionare indipendentemente dal fatto che non sia mai esistito o sia stato rimosso in fase di esecuzione, ad esempio:

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

Esempi di utilizzo #

Ecco come puoi implementare uno ZPOP in Lua per rimuovere gli articoli con il rango più basso da un set ordinato:

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]

E come implementare ZREVPOP per rimuovere gli elementi con il rango più alto da un set ordinato:

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]

Altri esempi #

Restituendo un int :

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

Restituendo una string :

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

Restituzione di un List di stringhe:

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

Altri esempi possono essere trovati nei test Redis Eval Lua