redis-py で expire が付与されていない key を削除する
メモリが逼迫してきた redis で、本来存在しないはずの expire が付与されていない key を削除することになったので記録として。
key は session id として使われています。
環境
前提
- session id は英数小文字と数字が含まれていて、規則性がない
こんなの =>csodes5b5wenrvn0llbfcmme
使ったもの
インストールは pip で一発です。
スクリプトの概要
小文字と数字のランダムな文字列を生成してワイルドカードで keys をたたく
keys *
だと負荷がちょっとこわかったため、ちょっとずつやりたかったのです
*ランダム文字列の生成は こちら のものを使わせていただきました。
DELETE_COUNT の数だけ削除するが、keys コマンドでひっぱってくる数にバラつきがあるので、正確ではない
スクリプト
#!/usr/bin/env/python # -*- coding: utf-8 -*- import os, sys, string, random, time import redis from logging import getLogger import logging logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO) logger = getLogger(os.path.basename(__file__)) # configration HOST = sys.argv[1] PORT = 6379 DB = 0 RANDOM_COUNT = 3 DELETE_COUNT = 200000 def connect_redis(h=HOST, p=PORT, d=DB): try: r = redis.StrictRedis(host=h, port=p, db=d) except redis.ConnectionError: logger.error("failed to connect %s" % HOST) logger.exception(e) sys.exit(1) except Exception, e: logger.exception(e) sys.exit(2) finally: return r def get_redis_info(arg): r = connect_redis() info = r.info(arg) return info def check_delete_limit(DB,DELETE_LIMIT): i = get_redis_info("keyspace") if i.has_key("db%s" % DB) == True: d = i.get("db%s" % DB) k = d.get("keys") - d.get("expires") if k <= DELETE_LIMIT: logger.info("limit arrived. stop process.") sys.exit(0) def get_delete_limit(DB,DELETE_COUNT): i = get_redis_info("keyspace") if i.has_key("db%s" % DB) == True: d = i.get("db%s" % DB) k = d.get("keys") - d.get("expires") if k >= DELETE_COUNT: k -= DELETE_COUNT return k elif k == DELETE_COUNT: return k else: logger.info("nothing to delete any more.") sys.exit(0) def gen_rand_str(length, chars=None): if chars is None: chars = string.digits + string.lowercase return ''.join([random.choice(chars) for i in range(length)]) def delete_session_keys(HOST,PORT,DB,RANDOM_COUNT,DELETE_LIMIT): sample_key = "*" + gen_rand_str(RANDOM_COUNT) + '*' # get sample keys r = connect_redis() keys = r.keys(sample_key) if keys is None: logger.info("not found expire keys from sample key.") for k in keys: # skip key "RedisSessionStateStore:LockedSessions" if "LockedSessions" in k: logger.debug("skip delete. key => %s" % k) continue # check ttl each key logger.debug("ttl start") ttl = r.ttl(k) logger.debug("ttl end") if ttl == -1: # delete key r.delete(k) logger.info("delete key => %s" % k) time.sleep(0.1) else: logger.debug("this key has ttl. key => %s , ttl => %s" % (k, ttl)) if __name__ == '__main__': DELETE_LIMIT = get_delete_limit(DB,DELETE_COUNT) while True: check_delete_limit(DB, DELETE_LIMIT) delete_session_keys(HOST,PORT,DB,RANDOM_COUNT,DELETE_LIMIT)
実行
$ python delete_session_keys.py redisのIPアドレスなど delete key => s5zhfytghgdq5bjmkaaqhkf2 delete key => sedhrhftw13jblpawgbvrh4k delete key => s4rewrbhex04fdujbytp3rw5 delete key => skwcm0jigx2arwqwf52sj21n this key has ttl. key => uxxprmpwyjpxqeots1q334lo , ttl => 10503 this key has ttl. key => djfw2py52croafz4gigun0l0 , ttl => 3606
以上です。