diary

日記です

master-slave構成のredisでttl(expire)を持つキーを使う方法

いろいろあってめんどい。

  1. ttlを指定したキーの実削除は、キーの参照があった際又は100ms毎に行われるttlを持つ全キーからのランダムルックアップによる検査により行われる
  2. 故に、短いttlを持つキーが多数存在する場合には、(実際に参照しない限り)実削除が間に合わなくなる(意図した時間に揮発しない)事がある

これだけならまあ参照すればいいんですが

  1. master-slave構成時、slaveから見たキーの削除はmaster側からの削除命令が無ければ行わず、ttl < 0となったキーに関してもこれは同様に扱われる
  2. 故に、揮発されるべきキーがslaveから参照されても、実削除は行われず、(master側の定期実削除が間に合っていない場合)slaveはそのまま揮発されているべきキーの値を返してしまう

対応策は

  1. masterに対してgetを投げる
    master-slaveとは何だったのか…
  2. redis.hのREDIS_EXPIRELOOKUPS_PER_CRONを増やす
    100msごとにルックアップするキーの数を増やす。
    揮発されるまでの時間は短くなるがCPU負荷は上がる。
    redisの再コンパイルも必要だし、そもそも根本的な解決にはならない
  3. slaveからのキー参照時に、必ずttlコマンドによる生存時間確認を行う
    実削除はされなくても、該当キーのttlは(unixtimeで時間を持っている為)正常にカウントダウンされる。
    このため、この現象が起こってしまったキーに対してttlコマンドを打つと0-1を返す。
    これを利用して論理的にそのキーがexpireしたことを確認する。
    ttlを使わないキーもttl=0ttl=-1であることを留意しないと辛いことになる。
  4. ttl使うキーを減らすか無くす、又はそのような用途にredisを使わない

まああんまりttlに期待しない設計にするのが一番よいです。memcachedのslab程あんまり断片化に気を使わないでもいいしそこそこ速いしレプリケーション出来るしmemcached代替として使っちゃいたくなるけどまあ落とし穴はありますよ、と。

redisはコード平易で読みやすくていいですねというお話でした(?)

2013/3/13 訂正:

正しくはttl=0じゃなくて-1です。ただ、expireしたのもttlを持たないのも両方-1返すんで、区別出来ないのには変わりなし