Redis Python - как удалить все ключи в соответствии с определенным шаблоном В python, без повторения python
Я пишу команду управления 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)
Но это может привести к потере скорости и неправильному использованию его возможностей. Есть ли питоническое решение с py-redis, без итерации и/или кли?
Спасибо!
Ответы
Ответ 1
Я думаю, что
for key in x: cache.delete(key)
довольно хорош и лаконичен. delete
действительно хочет по одному ключу за раз, поэтому вам нужно зациклиться.
В противном случае этот предыдущий вопрос и ответ указывает вам на решение на основе lua.
Ответ 2
Используйте итераторы SCAN: https://pypi.python.org/pypi/redis
for key in r.scan_iter("prefix:*"):
r.delete(key)
Ответ 3
Из Документация
delete(*names)
Delete one or more keys specified by names
Это просто требует аргумент за каждый ключ для удаления, а затем он расскажет вам, сколько из них было найдено и удалено.
В случае вашего кода выше я считаю, что вы можете просто сделать:
redis.delete(*x)
Но я признаю, что я новичок в python, и я просто делаю:
deleted_count = redis.delete('key1', 'key2')
Ответ 4
cache.delete(*keys)
решение Dirk отлично работает, но убедитесь, что ключи не пустые, чтобы избежать redis.exceptions.ResponseError: wrong number of arguments for 'del' command
.
Если вы уверены, что всегда будете получать результат: cache.delete(*cache.keys('prefix:*') )
Ответ 5
Вот полный рабочий пример, используя 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
для каждого ключа.
Ура!
UPDATE:
С момента написания ответа я начал использовать функцию конвейерной обработки 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 (Best Performing):
Если вы используете 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 chunks и небольшой idle 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
Ответ 6
Согласно моему тесту, если я использую решение scan_iter
(как писал Алекс Тодерита), это будет стоить слишком много времени.
Поэтому я предпочитаю использовать:
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:*
это шаблон.
ссылается на: fooobar.com/questions/16888/...
Ответ 7
Btw, для django-redis вы можете использовать следующее (из https://niwinz.github.io/django-redis/latest/):
from django.core.cache import cache
cache.delete_pattern("foo_*")
Ответ 8
Используйте delete_pattern: https://niwinz.github.io/django-redis/latest/
from django.core.cache import cache
cache.delete_pattern("prefix:*")
Ответ 9
Вы можете использовать определенный шаблон, чтобы сопоставить все ключи и удалить их:
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)