51

redis キャッシングの一部を処理するための django 管理コマンドを作成しています。基本的に、特定のパターン (例: "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を使用したpythonicソリューションはありますか?

ありがとう!

4

9 に答える 9

28

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そのため、キーごとに a を実行するのがおそらく最善です。

乾杯!

アップデート:

回答を書いて以来、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):
        pipe.delete(key)
        count += 1
    pipe.execute()
    return count

UPDATE2 (最高のパフォーマンス):

scanの代わりにを使用するとscan_iter、チャンク サイズを制御し、独自のロジックを使用してカーソルを反復処理できます。これは、特に多くのキーを処理する場合に、はるかに高速であるようにも見えます。これにパイプラインを追加すると、すべてが生成されるまで実行コマンドを 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
于 2017-07-17T20:55:51.090 に答える
10

ドキュメントから

delete(*names)
    Delete one or more keys specified by names

これは、削除するキーごとの引数が必要なだけで、そのうちのいくつが見つかって削除されたかがわかります。

上記のコードの場合、次のことができると思います。

    redis.delete(*x)

しかし、私はPythonが初めてであることを認めます。

    deleted_count = redis.delete('key1', 'key2')
于 2016-05-17T02:34:40.490 に答える
3

ところで、django-redis の場合、以下を使用できます ( https://niwinz.github.io/django-redis/latest/から):

from django.core.cache import cache
cache.delete_pattern("foo_*")
于 2016-02-25T14:15:54.917 に答える
2

特定のパターンを使用して、すべてのキーを照合して削除できます。

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)
于 2018-09-12T09:55:38.403 に答える