bauer's diary

凡人の凡人による凡人のための備忘録

Redisでttlを指定していないキーを探し、削除する

1. はじめに

Redisを長く運用していると、いつの間にかSwap Usageが高騰したり、Freeable Memoryが枯渇してきて問題になることがあります。
そんなとき様々な原因が考えられますが、その一つにTTLを指定していないキーが容量を逼迫していたときの対処についてご紹介します。

2. Redis TTL とは

ttl コマンドでキーの有効期間を確認します。
有効期間を設定する場合は expire コマンドで指定します。

有効期間が設定されていないキーからは -1 が返ります。
存在しないキーからは -2 が返ります。
※Redis 2.6以前の場合は存在しないキーも -1 が返ります。
下記は使用例です。

> set key "hage"
OK
> get fuga
"hage"

// キーにTTL(単位はseconds)をセット
> expire key 100
(integer) 1

// TTLを確認
> ttl key
(integer) 98
> ttl key
(integer) 95

3. TTLが指定されていないキーを探す

ざっくりとTTLが指定されていないキーの数を確認する場合は、 info コマンドが有効です。
ここでは2つのキー全てがTTL未指定ということになります。

% redis-cli -h localhost info Keyspace
# Keyspace
db0:keys=2,expires=0,avg_ttl=0

それぞれどのようなキーなのか、Luaスクリプトを作成して探します。
ファイル名は find.lua とします。

local t = {}
local i = 1
for _, v in ipairs(redis.call('keys', 'prefix*')) 
do
  if redis.call('TTL',v) == -1 then
    t[i] = v
    i = i + 1
  end
end
return t
実行

db指定は必要であれば。

% redis-cli -h [redis-host] -n 0 --eval find.lua
結果

標準出力にキーが羅列していきます。

1) "hage"
2) "hagee"

4. TTLが指定されていないキーを削除する

色々やり方はあるかと思いますが、TTLが指定されていないキーに対してdelコマンドを羅列するシェルを生成するスクリプトにしました。
ファイル名は del_output.lua とします。

local t = {}
t[1] = "#!/bin/sh"
local i = 2
local cmd = "redis-cli -h [redis-host] del "
for _, v in ipairs(redis.call('keys', 'prefix*')) 
do
  if redis.call('TTL',v) == -1 then
    -- redis-cliのコマンドのターゲットをダブルクォーテーションで囲むため、keyの中のダブルクォーテーションをエスケープ
    local _v = string.gsub(v, '\"', '\\"')
    t[i] = cmd.."\"".._v.."\""
    i = i + 1
  end
end
return t
実行

シェルを生成します。

% redis-cli -h [redis-host] --eval del_output.lua > del_exec.sh

% chmod 755 del_exec.sh
% ./del_exec.sh
結果

削除が成功していくとこのように出力されるかと思います。

(integer) 1
(integer) 1
(integer) 1
(integer) 1

これでお掃除は完了です。

5. 最後に

運用期間が長いサービスだと、こういった作業が必要になることも出てくると思うので、キー毎に適切な有効期間を設定しておくことをオススメします。