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

RedisClient LUA API'er

IRedisClient API'er til redis server-side LUA-understøttelse er blevet re-faktoreret til de mere brugervenlige API'er nedenfor:

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'en nedenfor returnerer de første 10 resultater, der matcher key:* mønster:

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

C# Streaming API'et ovenfor kræver dog et ukendt antal Redis-operationer (afgrænset til antallet af nøgler i Redis) for at fuldføre anmodningen. Antallet af SCAN-opkald kan reduceres ved at vælge en højere pageSize at bede Redis om at scanne flere nøgler, hver gang SCAN-operationen kaldes.

Da antallet af API-kald har potentiale til at resultere i et stort antal Redis-operationer, kan det ende med at give en uacceptabel forsinkelse på grund af latensen af ​​flere afhængige fjernnetværksopkald. En nem løsning er i stedet at få de flere SCAN-opkald udført i processen på Redis-serveren, hvilket eliminerer netværksforsinkelsen for flere SCAN-opkald, f.eks.:

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 returnerer dette komplekse LUA-tabelsvar i Children samling af RedisText Svar.

Alternativ kompleks API-svar #

En anden måde at returnere komplekse datastrukturer i en LUA-operation er at serialisere resultatet som JSON

return cjson.encode(results)

Som du kan få adgang til som rå JSON ved at parse svaret som en streng med:

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

INFO

Dette er også den tilgang, der bruges i Redis Reacts RedisServices.

ExecCachedLua #

ExecCachedLua er en praktisk API på højt niveau, der eliminerer den bogføring, der kræves for at udføre højtydende server-LUA-scripts, som lider under mange af de problemer, som RDBMS-lagrede procedurer har, som afhænger af den allerede eksisterende tilstand i RDBMS'et, som skal opdateres med seneste version af den lagrede procedure.

Med Redis LUA har du enten mulighed for at sende, parse, indlæse og derefter udføre hele LUA-scriptet, hver gang det kaldes, eller alternativt kan du forudindlæse LUA-scriptet i Redis én gang ved StartUp og derefter udføre det ved hjælp af Scriptets SHA1-hash. Problemet med dette er, at hvis Redis-serveren ved et uheld skylles, står du tilbage med en ødelagt applikation, der er afhængig af et allerede eksisterende script, der ikke længere er der. Den nye ExecCachedLua API giver det bedste fra begge verdener, hvor det altid vil udføre det kompilerede SHA1-script, spare båndbredde og CPU, men også genskabe LUA-scriptet, hvis det ikke længere eksisterer.

Du kan i stedet udføre det kompilerede LUA-script ovenfor ved dets SHA1-id, som fortsætter med at fungere, uanset om det aldrig har eksisteret eller blev fjernet under kørsel, f.eks.:

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

Eksempler på brug #

Sådan kan du implementere en ZPOP i Lua for at fjerne elementerne med den laveste rang fra et sorteret sæt:

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]

Og hvordan man implementerer ZREVPOP for at fjerne elementer med den højeste rang fra et sorteret sæt:

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]

Andre eksempler #

Returnerer en int :

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

Returnerer en string :

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

Returnerer en List af strenge:

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

Flere eksempler kan findes i Redis Eval Lua-testene


  1. Hvordan tørrer man Heroku Redis?

  2. Hvordan kan du fjerne alle dokumenter fra en samling med Mongoose?

  3. Få alle nøgler i Redis-databasen med python

  4. Automatisk MongoDB Backup