Redis Python - 如何根据特定模式删除所有键在python中,没有python迭代

时间:2014-02-23 22:01:55

标签: python redis redis-py

我正在编写一个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的速度和滥用其功能。有没有py-redis的pythonic解决方案,没有迭代和/或cli?

谢谢!

9 个答案:

答案 0 :(得分:23)

使用SCAN迭代器:https://pypi.python.org/pypi/redis

for key in r.scan_iter("prefix:*"):
    r.delete(key)

答案 1 :(得分:13)

我认为

 for key in x: cache.delete(key)

非常简洁。 delete一次只想要一个密钥,所以你必须循环。

否则,此previous question and answer会将您指向基于lua的解决方案。

答案 2 :(得分:9)

来自Documentation

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

这只是想要一个每个键的参数被删除,然后它会告诉你有多少被发现和删除。

如果您的代码在上面,我相信您可以这样做:

    redis.delete(*x)

但我承认我是python的新手,我只是这样做:

    deleted_count = redis.delete('key1', 'key2')

答案 3 :(得分:6)

以下是使用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的流水线功能在一个请求中发送所有命令并避免网络延迟:

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

UPDATE2(表演最佳):

如果使用scan而不是scan_iter,则可以使用自己的逻辑控制块大小并迭代游标。这似乎也快得多,特别是在处理许多键时。如果您为此添加流水线操作,您将获得一些性能提升,10-25%,具体取决于块大小,以内存使用为代价,因为在生成所有内容之前,您不会将执行命令发送到Redis。所以我坚持扫描:

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

答案 4 :(得分:4)

Dirk的

cache.delete(*keys)解决方案运行正常,但请确保密钥不为空以避免redis.exceptions.ResponseError: wrong number of arguments for 'del' command

如果您确定总能得到结果:cache.delete(*cache.keys('prefix:*') )

答案 5 :(得分:3)

根据我的测试,如果我使用scan_iter解决方案(Alex Toderita wrote),则会花费太多时间。

因此,我更喜欢使用:

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:*是模式。

指的是: https://stackoverflow.com/a/16974060

答案 6 :(得分:2)

顺便说一句,对于django-redis,您可以使用以下内容(来自https://niwinz.github.io/django-redis/latest/):

from django.core.cache import cache
cache.delete_pattern("foo_*")

答案 7 :(得分:1)

您可以使用特定的模式来匹配所有键并将其删除:

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)

答案 8 :(得分:0)

使用delete_pattern:https://niwinz.github.io/django-redis/latest/

from django.core.cache import cache
cache.delete_pattern("prefix:*")