sql >> Base de Datos >  >> NoSQL >> Redis

API de RedisClient LUA

El IRedisClient Las API para la compatibilidad con LUA del lado del servidor de redis se han refactorizado en las API más fáciles de usar a continuación:

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

ESCANEO eficiente en LUA #

La siguiente API de C# devuelve los primeros 10 resultados que coinciden con la key:* patrón:

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

Sin embargo, la API de transmisión de C# anterior requiere una cantidad desconocida de operaciones de Redis (limitada a la cantidad de claves en Redis) para completar la solicitud. El número de llamadas SCAN se puede reducir eligiendo un pageSize más alto para decirle a Redis que escanee más claves cada vez que se llama a la operación SCAN.

Dado que la cantidad de llamadas a la API tiene el potencial de generar una gran cantidad de operaciones de Redis, puede terminar generando un retraso inaceptable debido a la latencia de varias llamadas de red remotas dependientes. Una solución fácil es, en cambio, realizar varias llamadas de SCAN en el proceso en el servidor de Redis, lo que elimina la latencia de red de varias llamadas de SCAN, por ejemplo:

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

El ExecLua API devuelve esta respuesta de tabla LUA compleja en Children colección de RedisText Respuesta.

Respuesta de API compleja alternativa n.º

Otra forma de devolver estructuras de datos complejas en una operación LUA es serializar el resultado como JSON

return cjson.encode(results)

Al que puede acceder como JSON sin formato analizando la respuesta como una cadena con:

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

INFORMACIÓN

Este es también el enfoque utilizado en RedisServices de Redis React.

ExecCachedLua #

ExecCachedLua es una API conveniente de alto nivel que elimina la contabilidad requerida para ejecutar secuencias de comandos LUA de servidor de alto rendimiento que sufre muchos de los problemas que tienen los procedimientos almacenados de RDBMS que dependen del estado preexistente en el RDBMS que necesita ser actualizado con el última versión del procedimiento almacenado.

Con Redis LUA, tiene la opción de enviar, analizar, cargar y luego ejecutar el script LUA completo cada vez que se llama o, alternativamente, puede precargar el script LUA en Redis una vez en el inicio y luego ejecutarlo usando el hash SHA1 del script. El problema con esto es que si el servidor Redis se vacía accidentalmente, se queda con una aplicación rota que depende de un script preexistente que ya no está allí. El nuevo ExecCachedLua La API ofrece lo mejor de ambos mundos, donde siempre ejecutará el script SHA1 compilado, ahorrando ancho de banda y CPU, pero también volverá a crear el script LUA si ya no existe.

En su lugar, puede ejecutar el script LUA compilado anteriormente por su identificador SHA1, que continúa funcionando independientemente de si nunca existió o si se eliminó en tiempo de ejecución, por ejemplo:

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

Ejemplos de uso #

Así es como puede implementar un ZPOP en Lua para eliminar los elementos con el rango más bajo de un conjunto ordenado:

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]

Y cómo implementar ZREVPOP para eliminar elementos con el rango más alto de un conjunto ordenado:

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]

Otros ejemplos #

Devolviendo un int :

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

Devolviendo una string :

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

Devolviendo una List de cadenas:

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

Se pueden encontrar más ejemplos en las pruebas de Redis Eval Lua