【问题标题】:Redis Python - how to delete all keys according to a specific pattern In python, without python iteratingRedis Python - 如何在python中根据特定模式删除所有键,无需python迭代
【发布时间】:2014-03-25 09:01:11
【问题描述】:

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

我知道我可以使用 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?

谢谢!

【问题讨论】:

    标签: python django redis django-cache redis-py


    【解决方案1】:

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

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

    【讨论】:

    【解决方案2】:

    这是一个使用 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):
            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
    

    以下是一些基准:

    5k 块使用繁忙的 Redis 集群:

    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 块和一个小的空闲开发 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
    

    【讨论】:

    • 我提供了一个完整的工作示例。也希望其他人评论 scan_iter 与批量删除
    • 很好的答案,这应该是正确的答案。今天我自己实际上需要这个答案,并且我更喜欢你的答案。尽管您的示例中有一些小错误,例如您的第一次更新中没有 ns_keys 变量,以及第二次更新中的 ::。
    • 谢谢,但我实际上并没有在 prod 中使用扫描,因为它太慢了,相反,我最终缓存了命名空间中的每个键并以这种方式进行批量删除。我知道这似乎有点矫枉过正,但性能最好,因为您根本不必扫描缓存。
    • 仅供参考,第三个示例的第 13 行存在语法错误;您的 while 条件末尾有两个冒号 (:)。
    • cursor = '0'while cursor != 0 之间的输入很尴尬。你可以使用cursor = Nonecache.scan(cursor=cursor or 0, ...) 让它稍微好一点
    【解决方案3】:

    我觉得

     for key in x: cache.delete(key)
    

    非常好,简洁。 delete 真的想要一个键,所以你必须循环。

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

    【讨论】:

    • 使用 redis-python 包你可以这样做:cache.delete(*keys)
    【解决方案4】:

    来自Documentation

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

    这只是想删除每个键的参数,然后它会告诉您找到并删除了其中的多少。

    对于您上面的代码,我相信您可以这样做:

        redis.delete(*x)
    

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

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

    【讨论】:

      【解决方案5】:

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

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

      【讨论】:

      【解决方案6】:

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

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

      【讨论】:

      • 这个问题属于redis,所以django缓存框架超出范围。
      【解决方案7】:

      根据我的测试,如果我使用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

      【讨论】:

        【解决方案8】:

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

        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)
        

        【讨论】:

          【解决方案9】:

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

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2023-03-21
            • 2018-08-09
            • 1970-01-01
            • 1970-01-01
            • 2021-02-04
            • 2014-03-15
            相关资源
            最近更新 更多