Redis Python—如何根据Python中的特定模式删除所有键,而无需Python迭代

Redis Python—如何根据Python中的特定模式删除所有键,而无需Python迭代,python,django,redis,django-cache,redis-py,Python,Django,Redis,Django Cache,Redis Py,我正在编写一个django管理命令来处理一些redis缓存。基本上,我需要选择所有符合特定模式的键(例如:“prefix:*”)并删除它们 我知道我可以使用cli来做到这一点: redis-cli KEYS "prefix:*" | xargs redis-cli DEL 但我需要在应用程序中执行此操作。所以我需要使用python绑定(我使用的是PyRedis)。我已尝试将列表输入delete,但失败: from common.redis_client import get_redis_cli

我正在编写一个django管理命令来处理一些redis缓存。基本上,我需要选择所有符合特定模式的键(例如:“prefix:*”)并删除它们

我知道我可以使用cli来做到这一点:

redis-cli KEYS "prefix:*" | xargs redis-cli DEL
但我需要在应用程序中执行此操作。所以我需要使用python绑定(我使用的是PyRedis)。我已尝试将列表输入delete,但失败:

from common.redis_client import get_redis_client
cache = get_redis_client()
x = cache.keys('prefix:*') 

x == ['prefix:key1','prefix:key2'] # True
#现在呢

#返回0。没有删除任何内容

我知道我可以在x上迭代:

for key in x:
   cache.delete(key)
但这将失去redis惊人的速度,并滥用其功能。是否有py-redis的pythonic解决方案,没有迭代和/或cli

谢谢

我认为

 for key in x: cache.delete(key)
很好,很简洁
delete
一次只需要一个键,所以必须循环

否则,这会将您指向基于lua的解决方案。

缓存。删除(*键)
Dirk的解决方案工作正常,但请确保键不是空的,以避免出现
redis.exceptions.ResponseError:“del”命令的参数数目错误。


如果您确信始终会得到一个结果:
cache.delete(*cache.keys('prefix:'))

使用扫描迭代器:

这只需要删除每个键的一个参数,然后它会告诉您找到并删除了多少个参数

对于上述代码,我相信您可以:

    redis.delete(*x)
但我承认我对python是新手,我只是:

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

下面是一个完整的工作示例,使用:

您还可以执行
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):
        pipe.delete(key)
        count += 1
    pipe.execute()
    return count
更新2(最佳性能):

如果使用
scan
而不是
scan\u iter
,则可以控制块大小,并使用自己的逻辑遍历光标。这似乎也要快得多,尤其是在处理许多键时。如果将管道添加到这一点上,您将获得一点性能提升,10-25%取决于块大小,这是以内存使用为代价的,因为在生成所有内容之前,您不会向Redis发送execute命令。所以我坚持扫描:

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):


根据我的测试,如果我使用
scan\u iter
solution(as),将花费太多时间

因此,我更喜欢使用:

from redis.connection import ResponseError

try:
    redis_obj.eval('''return redis.call('del', unpack(redis.call('keys', ARGV[1])))''', 0, 'prefix:*')
except ResponseError:
    pass
前缀:是模式


指: 使用删除模式:


可以使用特定模式匹配所有关键点并删除它们:

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)

使用redis python包,您可以这样做:
cache.delete(*keys)
不要在prod中使用
cache.keys()
,它的目的是为了调试:这个问题属于redis,因此django缓存框架超出了范围。我提供了一个完整的工作示例。我也希望其他人对扫描iter vs bulk delete做出评论。答案很好,这应该是正确的答案。今天我自己确实需要这个答案,我更喜欢你的答案。虽然您的示例中存在一些小错误,例如第一次更新中没有ns_keys变量,第二次更新中有::。谢谢,但实际上我没有在prod中使用扫描,因为它太慢了,我最终会缓存命名空间中的每个键,并以这种方式进行批量删除。我知道这似乎有些过分,但性能最好,因为根本不需要扫描缓存。仅供参考,第三个示例的第13行出现语法错误;while条件结束时有两个冒号(:)。UPDATE2在生产中是无价的(默认值
chunk\u size
会慢一个数量级!):chunk\u size:execute\u time:5:1m 44.8s 50:17.0s 500:7.3s 5000:从socketdjango redis读取超时实现了delete\u模式,其功能与此非常类似,请参阅。
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
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
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
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
from redis.connection import ResponseError

try:
    redis_obj.eval('''return redis.call('del', unpack(redis.call('keys', ARGV[1])))''', 0, 'prefix:*')
except ResponseError:
    pass
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)