In Linux we trust!

Redis, 2 особенности Lua

Две особенности написания скриптов в Lua под Redis.

Детерминизм скриптов

Т.к. Redis обеспечивает сохранность данных через логгирование произведенных над данными операций, то в Lua запрещены недетерминированные вывозы (например, SRANDMEMBER или TIME). За исключением режима read-only, в котором данные не изменяются. Также в Redis 5 было изменено поведение, за более подробной информацией обращайтесь к документации.

Компактность данных

В Redis фактически нету numeric типов, все сохраняется в виде строк. Например, для сохранения числа 35000 будет использовано 5 байт вместо 2. Т.о. для компактного сохранения множественных чисел можно использовать Lua для преобразования чисел. Для примера рассмотрим случай, когда ID записи получается через INCR и этот ID вставляется в несколько мест.

-- test_int2raw.lua
function string.int2raw(str)
    str = string.format("%08X", tonumber(str))
    return (str:gsub("..", function (cc)
        return string.char(tonumber(cc, 16))
    end))
end
function string.raw2int(str)
    str = str:gsub(".", function (c)
        return string.format("%02X", string.byte(c))
    end)
    return tonumber(str, 16)
end
local data = tostring(ARGV[1])
local raw = tonumber(ARGV[2])
local id = redis.call("INCR", "id")
local id_raw = id
if raw == 1 then
    id_raw = string.int2raw(id_raw)
end
local hmset_args = {"id:" .. id_raw, "_", data}
redis.call("HMSET", unpack(hmset_args))
redis.call("SADD", "_:" .. data, id_raw)
return id

Произведем пару проверок для миллиона записей:

$ redis-cli flushall
$ redis-cli -r 1000000 --eval test_int2raw.lua , _ 0 &>/dev/null
$ redis-cli info | grep used_memory_human
used_memory_human:183.88M
$ redis-cli flushall
$ redis-cli -r 1000000 --eval test_int2raw.lua , _ 1 &>/dev/null
$ redis-cli info | grep used_memory_human
used_memory_human:168.46M

Экономия около 8%, это при том, что ID записи фигурирует в 2-х структурах данных. При большем количестве структур экономия может быть больше. В листинг я также добавил обратную функцию string.raw2int.

Всем удачи!