Django redisキャッシュの一部を処理する管理コマンドを書いています。基本的に、特定のパターンを確認するすべてのキーを選択する必要があります(例: "prefix:*")それらを削除します。
私はそれを行うためにCLIを使用できることを知っています:
redis-cli KEYS "prefix:*" | xargs redis-cli DEL
しかし、私はアプリ内からこれを行う必要があります。だから私はpythonバインディングを使用する必要があります(私はpy-redisを使用しています)。
from common.redis_client import get_redis_client
cache = get_redis_client()
x = cache.keys('prefix:*')
x == ['prefix:key1','prefix:key2'] # True
# そしていま
cache.delete(x)
#は0を返します。何も削除されません
私はxを反復できることを知っています:
for key in x:
cache.delete(key)
しかし、それはredisの驚くべき速度を失い、その機能を誤用します。反復および/またはCLIなしで、py-redisを使用したPythonソリューションがありますか?
ありがとう!
私は思います
for key in x: cache.delete(key)
かなり簡潔です。 delete
は実際には一度に1つのキーが必要なので、ループする必要があります。
それ以外の場合、この 前の質問と回答 はluaベースのソリューションを示します。
SCANイテレータを使用します: https://pypi.python.org/pypi/redis
for key in r.scan_iter("prefix:*"):
r.delete(key)
ドキュメント から
delete(*names) Delete one or more keys specified by names
これは、キーごとに削除する引数を必要とするだけで、それらのどれだけが見つかって削除されたかを示します。
上記のコードの場合、私はあなたがちょうどできると信じています:
redis.delete(*x)
しかし、私はpythonが初めてだということを認めます。
deleted_count = redis.delete('key1', 'key2')
py-redis を使用した完全な動作例を次に示します。
from redis import StrictRedis
cache = StrictRedis()
def clear_ns(ns):
"""
Clears a namespace
:param ns: str, namespace i.e your:prefix
:return: int, cleared keys
"""
count = 0
ns_keys = ns + '*'
for key in cache.scan_iter(ns_keys):
cache.delete(key)
count += 1
return count
scan_iter
を実行してすべてのキーをメモリに取得し、すべてのキーをdelete
に渡して一括削除することもできますが、より大きなネームスペースでは十分なメモリを使用できます。したがって、おそらく各キーに対してdelete
を実行するのが最善です。
乾杯!
更新:
答えを書いてから、私はredisのパイプライン機能を使用して、すべてのコマンドを1つのリクエストで送信し、ネットワーク遅延を回避し始めました。
from redis import StrictRedis
cache = StrictRedis()
def clear_cache_ns(ns):
"""
Clears a namespace in redis cache.
This may be very time consuming.
:param ns: str, namespace i.e your:prefix*
:return: int, num cleared keys
"""
count = 0
pipe = cache.pipeline()
for key in cache.scan_iter(ns_keys):
pipe.delete(key)
count += 1
pipe.execute()
return count
更新2(最高のパフォーマンス):
scan_iter
の代わりにscan
を使用する場合、独自のロジックを使用してチャンクサイズを制御し、カーソルを反復処理できます。また、これは、特に多くのキーを処理する場合、はるかに高速であるようです。これにパイプラインを追加すると、すべてが生成されるまで実行コマンドをRedisに送信しないため、メモリ使用量を犠牲にして、チャンクサイズに応じて10〜25%のパフォーマンスが少し向上します。だから私はスキャンで立ち往生しました:
from redis import StrictRedis
cache = StrictRedis()
CHUNK_SIZE = 5000
def clear_ns(ns):
"""
Clears a namespace
:param ns: str, namespace i.e your:prefix
:return: int, cleared keys
"""
cursor = '0'
ns_keys = ns + '*'
while cursor != 0::
cursor, keys = cache.scan(cursor=cursor, match=ns_keys, count=CHUNK_SIZE)
if keys:
cache.delete(*keys)
return True
ベンチマークは次のとおりです。
忙しいRedisクラスターを使用した5kチャンク:Done removing using scan in 4.49929285049 Done removing using scan_iter in 98.4856731892 Done removing using scan_iter & pipe in 66.8833789825 Done removing using scan & pipe in 3.20298910141
5kチャンクと小さなアイドルdev redis(localhost):Done removing using scan in 1.26654982567 Done removing using scan_iter in 13.5976779461 Done removing using scan_iter & pipe in 4.66061878204 Done removing using scan & pipe in 1.13942599297
cache.delete(*keys)
Dirkのソリューションは正常に動作しますが、_redis.exceptions.ResponseError: wrong number of arguments for 'del' command
_を避けるためにキーが空でないことを確認してください。
常に結果が得られることが確実な場合:cache.delete(*cache.keys('prefix:*') )
私のテストによると、scan_iter
ソリューションを使用すると時間がかかりすぎます( Alex Toderitaが書いたように )。
したがって、私は使用することを好む:
from redis.connection import ResponseError
try:
redis_obj.eval('''return redis.call('del', unpack(redis.call('keys', ARGV[1])))''', 0, 'prefix:*')
except ResponseError:
pass
prefix:*
はパターンです。
ところで、Django-redisの場合、次を使用できます( https://niwinz.github.io/Django-redis/latest/ から):
from Django.core.cache import cache
cache.delete_pattern("foo_*")
Delete_patternを使用: https://niwinz.github.io/Django-redis/latest/
from Django.core.cache import cache
cache.delete_pattern("prefix:*")
特定のパターンを使用してすべてのキーを照合し、それらを削除できます。
import redis
client = redis.Redis(Host='192.168.1.106', port=6379,
password='pass', decode_responses=True)
for key in client.keys('prefix:*'):
client.delete(key)