sql >> Databasteknik >  >> NoSQL >> Redis

RedisClient LUA API:er

IRedisClient API:er för redis server-side LUA-stöd har omfaktorerats till de mer användarvänliga API:erna nedan:

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

Effektiv SCAN i LUA #

C# API nedan returnerar de första 10 resultaten som matchar key:* mönster:

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

Men C# Streaming API ovan kräver ett okänt antal Redis Operations (avgränsat till antalet nycklar i Redis) för att slutföra begäran. Antalet SCAN-samtal kan minskas genom att välja en högre pageSize att be Redis att skanna fler nycklar varje gång SCAN-operationen anropas.

Eftersom antalet API-anrop har potential att resultera i ett stort antal Redis-operationer, kan det sluta med en oacceptabel fördröjning på grund av fördröjningen av flera beroende fjärrnätverksanrop. En enkel lösning är att istället låta flera SCAN-anrop utföras i processen på Redis Server, vilket eliminerar nätverkslatensen för flera SCAN-samtal, t.ex.:

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 returnerar detta komplexa LUA-tabellsvar i Children samling av RedisText Svar.

Alternativt komplext API-svar #

Ett annat sätt att returnera komplexa datastrukturer i en LUA-operation är att serialisera resultatet som JSON

return cjson.encode(results)

Som du kan komma åt som rå JSON genom att analysera svaret som en sträng med:

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

INFO

Detta är också metoden som används i Redis Reacts RedisServices.

ExecCachedLua #

ExecCachedLua är ett bekvämt högnivå-API som eliminerar bokföringen som krävs för att exekvera högpresterande server LUA-skript som lider av många av de problem som RDBMS lagrade procedurer har som beror på befintligt tillstånd i RDBMS som behöver uppdateras med senaste versionen av den lagrade proceduren.

Med Redis LUA har du antingen möjlighet att skicka, analysera, ladda och sedan exekvera hela LUA-skriptet varje gång det anropas eller alternativt kan du förladda LUA-skriptet till Redis en gång vid StartUp och sedan köra det med skriptets SHA1-hash. Problemet med det här är att om Redis-servern av misstag töms så står du kvar med en trasig applikation som förlitar sig på ett redan existerande skript som inte längre finns där. Den nya ExecCachedLua API ger det bästa av två världar där det alltid kommer att köra det kompilerade SHA1-skriptet, spara bandbredd och CPU, men också återskapa LUA-skriptet om det inte längre finns.

Du kan istället köra det kompilerade LUA-skriptet ovan med dess SHA1-identifierare, som fortsätter att fungera oavsett om det aldrig funnits eller togs bort under körning, t.ex.:

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

Användningsexempel #

Så här kan du implementera en ZPOP i Lua för att ta bort objekten med lägst rang från en sorterad uppsättning:

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]

Och hur man implementerar ZREVPOP för att ta bort objekt med högsta rang från en sorterad uppsättning:

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]

Andra exempel #

Returnerar en int :

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

Returnerar en string :

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

Returnera en List av strängar:

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

Fler exempel finns i Redis Eval Lua-testerna


  1. Skydda dina data med ClusterControl

  2. MongoDB - Aggregation - För att få unika föremål i array

  3. Hur löser man mongoDB-relaterade problem effektivt?

  4. Hur hanterar MongoDB samtidiga uppdateringar?