воскресенье, сентября 07, 2014

Redis: Удаление атрибутов hash по маске

Проблема:
Удалить поля HSET используя маску. Например
HMSET myhset f1 "v1" f2 "v2" x "v3" y "v4"
Хочется написать, что-то вида
HDEL myhset f*
Можно использовать HSCAN и несколько батчей, для удаления, однако он не гарантирует атомарность. Если последнее вам важно, на помощь приходит LUA:
-- ARGV[1] - hash key
-- ARGV[1] - lua pattern 
local fields = redis.call("HKEYS", ARGV[1]);
local retVal = {};
for key, value in pairs(fields) do
    if (string.match(value, ARGV[2])) then
        table.insert(retVal, value);
        redis.call("HDEL", ARGV[1], value);
    end
end

return retVal;
Скрипт имеет сложность O(n). Скрипт вернёт список полей, которые были удалены из HSET. Обратите внимание на string.match в LUA, чтобы понять что вам доступно помимо * в образце.

Вот как может выглядеть использование, если вы используете php и phpredis:
$r = new Redis();
$r->connect('127.0.0.1');

for ($i = 1; $i < 1000; $i++) {
    $r->hSet('myhash', 'aaa' . mt_rand(0, PHP_INT_MAX), 1);
}
$r->hSet('myhash', 'bad', 1);

$script = <<< SCR
    local fields = redis.call("HKEYS", ARGV[1]);
    local retVal = {};
    for key, value in pairs(fields) do
        if (string.match(value, ARGV[2])) then
            table.insert(retVal, value);
            redis.call("HDEL", ARGV[1], value);
        end
    end

    return retVal;
SCR;
var_dump($r->eval($script, ['myhash', '^b.+'])); 
Отправить комментарий