Redis
 sql >> база данни >  >> NoSQL >> Redis

RedisClient LUA API

IRedisClient Приложните програмни интерфейси (API) за поддръжка на LUA от страна на Redis от страна на сървъра са префакторирани в по-удобните за потребителя API по-долу:

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

Ефективно СКАНИРАНЕ в LUA #

API на C# по-долу връща първите 10 резултата, съответстващи на key:* модел:

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

Въпреки това C# Streaming API изисква неизвестен брой Redis операции (ограничени с броя на ключовете в Redis), за да завърши заявката. Броят на повикванията SCAN може да бъде намален чрез избор на по-висок pageSize за да кажете на Redis да сканира повече ключове всеки път, когато се извика операцията SCAN.

Тъй като броят на API повикванията има потенциал да доведе до голям брой операции на Redis, това може да доведе до неприемливо забавяне поради латентността на множество зависими отдалечени мрежови повиквания. Лесно решение е вместо това множеството SCAN повиквания да се извършват в процес на Redis Server, елиминирайки мрежовата латентност на множество SCAN повиквания, напр.:

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 връща този сложен отговор на LUA таблица в Children колекция от RedisText Отговор.

Алтернативен сложен отговор на API №

Друг начин за връщане на сложни структури от данни в LUA операция е да сериализирате резултата като JSON

return cjson.encode(results)

До който можете да получите достъп като необработен JSON, като анализирате отговора като низ с:

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

ИНФОРМАЦИЯ

Това е и подходът, използван в RedisServices на Redis React.

ExecCachedLua #

ExecCachedLua е удобен API на високо ниво, който елиминира счетоводството, необходимо за изпълнение на високопроизводителни сървърни LUA скриптове, който страда от много от проблемите, които имат съхраняваните процедури на RDBMS, които зависят от съществуващото състояние в RDBMS, което трябва да бъде актуализирано с най-новата версия на Съхранената процедура.

С Redis LUA имате възможност да изпращате, анализирате, зареждате и след това да изпълнявате целия LUA скрипт всеки път, когато бъде извикан, или алтернативно можете да заредите предварително LUA скрипта в Redis веднъж при стартиране и след това да го изпълните с помощта на SHA1 хеша на скрипта. Проблемът с това е, че ако сървърът Redis бъде прочистен случайно, оставате с повредено приложение, разчитащо на вече съществуващ скрипт, който вече не е там. Новият ExecCachedLua API предоставя най-доброто от двата свята, където винаги ще изпълнява компилирания SHA1 скрипт, спестявайки честотна лента и процесор, но също така ще създава повторно LUA скрипта, ако той вече не съществува.

Вместо това можете да изпълните компилирания LUA скрипт по-горе чрез неговия SHA1 идентификатор, който продължава да работи, независимо дали никога не е съществувал или е бил премахнат по време на изпълнение, напр.:

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

Примери за използване #

Ето как можете да внедрите ZPOP в Lua, за да премахнете елементите с най-нисък ранг от сортиран набор:

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]

И как да приложим ZREVPOP за да премахнете елементи с най-висок ранг от сортиран набор:

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]

Други примери #

Връщане на int :

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

Връщане на string :

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

Връщане на List на низове:

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

Още примери можете да намерите в тестовете Redis Eval Lua


  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. Времева сложност на zadd, когато стойността има резултат, по-висок от най-високия резултат, присъстващ в целевия сортиран набор

  2. Как да преместя база данни Redis от един сървър на друг?

  3. Мога ли да задам глобален TTL в redis?

  4. Redis като уникален генератор на атомни идентификатори - безопасен начин за уеб приложение за избягване на състезание

  5. Защо lua скрипт блокира redis-сървър?