RedisにてKeyにexpireで指定した時間を過ぎた時に,そのKeyのValueが欲しいということがあった.
Redis 2.8以上であれば,keyspace notificationsという仕組みがあり,Redis データセットに対するなんらかの変更イベントをPub/Subチャンネルで受け取ることが可能である.
詳しくは上記ドキュメントに書いてあるので,ここでは省略する.
notify-keyspace-events
をKEA
に設定しておくと,とりあえず発生するイベント全部取れる.
$ redis-cli
config set notify-keyspace-events KEA
Redisを起動するごとに設定するのは面倒なので,/etc/redis/redis.conf
にnotify-keyspace-events KEA
の一行を追加しておくとハッピー.
subscribe __keyevent@0__:expired
とかすればdatabase 0で有効期限が切れたKeyを検知できる.
検知した際,以下のようなデータが返ってくる.
{
'type': 'pmessage',
'pattern': b'__keyevent@0__:expired',
'channel': b'__keyevent@0__:expired',
'data': 'keyname'
}
ちなみにsubscribe __keyspace@0__:{キー}
とすれば特定のKeyに対して発生したイベントを検知できる.
ここで,注意するのが,返ってくるのはKeyだけでValueは返ってこず,当然既に削除されているのでValueを確認する術はない.
あまりスマートな方法とは言えないが,Valueも欲しい場合はSET shadowkey:keyname "" EX 100
みたいなことをして,実際のKeyにはexpireを設定せず,別途expire専用のKeyを保存する.
$ redis-cli
SET keyname "value"
SET shadowkey:keyname "" EX 100
// shadowkey:keynameが期限切れになった
GET keyname
DEL keyname
Pythonで書くと以下のような感じになる.
import redis
r = redis.Redis(host='localhost', port=6379, db=0)
pubsub = r.pubsub()
pubsub.psubscribe('__keyevent@0__:expired')
for msg in pubsub.listen():
if msg['pattern'] == b'__keyevent@0__:expired':
"""
{
'type': 'pmessage',
'pattern': b'__keyevent@0__:expired',
'channel': b'__keyevent@0__:expired',
'data': 'shadowkey:keyname'
}
"""
k = msg['data'].decode("utf-8").split(":")[1]
value = r.hget(k, 'somevalue').decode('utf-8')
# 何か処理をしてKeyを削除する
r.delete(k)