Redis
 sql >> Base de données >  >> NoSQL >> Redis

API RedisClient LUA

Le IRedisClient Les API pour la prise en charge de LUA côté serveur Redis ont été refactorisées dans les API plus conviviales ci-dessous :

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

SCAN efficace en LUA #

L'API C# ci-dessous renvoie les 10 premiers résultats correspondant à la key:* motif :

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

Cependant, l'API de streaming C# ci-dessus nécessite un nombre inconnu d'opérations Redis (limité au nombre de clés dans Redis) pour terminer la demande. Le nombre d'appels SCAN peut être réduit en choisissant un pageSize plus élevé pour dire à Redis de scanner plus de clés chaque fois que l'opération SCAN est appelée.

Étant donné que le nombre d'appels d'API peut entraîner un grand nombre d'opérations Redis, il peut finir par entraîner un retard inacceptable en raison de la latence de plusieurs appels réseau distants dépendants. Une solution simple consiste à effectuer à la place les multiples appels SCAN en cours sur le serveur Redis, éliminant ainsi la latence du réseau de plusieurs appels SCAN, par exemple :

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

Le ExecLua L'API renvoie cette réponse de table LUA complexe dans le Children collection du RedisText Réponse.

Réponse API complexe alternative #

Une autre façon de renvoyer des structures de données complexes dans une opération LUA consiste à sérialiser le résultat au format JSON

return cjson.encode(results)

Auquel vous pouvez accéder en tant que JSON brut en analysant la réponse en tant que chaîne avec :

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

INFORMATIONS

C'est également l'approche utilisée dans RedisServices de Redis React.

ExecCachedLua #

ExecCachedLua est une API de haut niveau pratique qui élimine la comptabilité requise pour l'exécution de scripts LUA de serveur hautes performances qui souffrent de nombreux problèmes rencontrés par les procédures stockées RDBMS qui dépendent de l'état préexistant dans le RDBMS qui doit être mis à jour avec le dernière version de la procédure stockée.

Avec Redis LUA, vous avez soit la possibilité d'envoyer, d'analyser, de charger puis d'exécuter l'intégralité du script LUA chaque fois qu'il est appelé, soit de précharger le script LUA dans Redis une fois au démarrage, puis de l'exécuter à l'aide du hachage SHA1 du script. Le problème avec cela est que si le serveur Redis est vidé accidentellement, vous vous retrouvez avec une application cassée reposant sur un script préexistant qui n'est plus là. Le nouveau ExecCachedLua L'API offre le meilleur des deux mondes où elle exécutera toujours le script SHA1 compilé, économisant de la bande passante et du processeur, mais recréera également le script LUA s'il n'existe plus.

Vous pouvez à la place exécuter le script LUA compilé ci-dessus par son identifiant SHA1, qui continue de fonctionner même s'il n'a jamais existé ou a été supprimé au moment de l'exécution, par exemple :

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

Exemples d'utilisation #

Voici comment implémenter un ZPOP en Lua pour supprimer les éléments les moins bien classés d'un ensemble trié :

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]

Et comment implémenter ZREVPOP pour supprimer les éléments avec le rang le plus élevé d'un ensemble trié :

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]

Autres exemples #

Retourner un int :

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

Renvoyer une string :

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

Renvoyer une List de chaînes :

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

Plus d'exemples peuvent être trouvés dans les tests Redis Eval Lua