monoの開発ブログ

Redis の巨大なハッシュやセットを削除する

Redis は基本的にシングルスレッドで動作しており、すべてのクライアントからのコマンドが直列化されて実行されます。基本的にはひとつひとつのコマンドは非常に短い時間で実行されるので、普段はこのことを意識する必要はないのですが、表題の件のように時間が掛かると厄介です。削除が完了するまでの間、他のコマンドが一切実行されなくなってしまいます。

これを回避するためには、コマンド1回あたりの実行時間が短くなるようにすれば OK です。今回の場合、すべての要素を1回のコマンドで削除するのではなく、中の要素を少しずつ削除していくようにします。

以下にセットの場合の例を示します。キーをいくつか引いてきて削除の繰り返しという流れはハッシュでも変わりません。

my $redis = Redis->new(...);

# rename して専用の名前空間に隔離
# これにより、rename した直後から元のキーは空きになる
my $key = 'safe_deleter:items:'.$redis->incr('safe_deleter:index');
$redis->rename('myset', $key);

my $cusor = 0;
while (1) {
    # 要素をスキャン
    my $members;
    ($cursor, $members) = $redis->sscan($key, $cursor, COUNT => 100);
    # 要素を削除
    $redis->srem($key, @$members) if @$members;
    # すべての要素をスキャンし終えたら終了
    last if $cursor == 0;
}
# 空になったセットを削除
$redis->del($key);

これにより、全体の処理時間は延びてしまいますが、コマンド1回あたりの時間は格段に短くなり、他のコマンドの実行を妨げて Redis が詰まることを防げます。

参考