Отримайте всі ключі в базі даних Redis за допомогою python


76

Існує публікація про команду Redis, щоб отримати всі доступні ключі, але я хотів би зробити це за допомогою Python.

Будь-який спосіб зробити це?

Відповіді:


114

Використовуйте scan_iter()

scan_iter()перевершує keys()для великої кількості клавіш, оскільки дає вам ітератор, яким ви можете скористатися, а не намагатися завантажити всі ключі в пам’ять.

У мене в реді було 1B записів, і я ніколи не міг отримати достатньо пам’яті, щоб повернути всі клавіші одночасно.

КЛЮЧІ СКАНУВАННЯ ОДИН ЗА ОДИН

Ось фрагмент python, за допомогою якого scan_iter()можна отримати всі ключі з магазину, що відповідають шаблону, і видалити їх по одному:

import redis
r = redis.StrictRedis(host='localhost', port=6379, db=0)
for key in r.scan_iter("user:*"):
    # delete the key
    r.delete(key)

СКАНУВАННЯ ПАРТІЯМИ

Якщо у вас є дуже великий список ключів для сканування - наприклад, більше> 100 тис. Ключів, буде ефективніше сканувати їх групами, наприклад:

import redis
from itertools import izip_longest

r = redis.StrictRedis(host='localhost', port=6379, db=0)

# iterate a list in batches of size n
def batcher(iterable, n):
    args = [iter(iterable)] * n
    return izip_longest(*args)

# in batches of 500 delete keys matching user:*
for keybatch in batcher(r.scan_iter('user:*'),500):
    r.delete(*keybatch)

Я порівняв цей скрипт і виявив, що використання пакета розміром 500 було в 5 разів швидше, ніж сканування ключів по одному. Я протестував різні розміри партії (3 500 500 000 5000) і виявив, що розмір партії 500 видається оптимальним.

Зауважте, що незалежно від того, чи використовуєте ви метод scan_iter()або keys(), операція не є атомною і може провалитися частково.

ЦІЛЬКИ УНИКНІТЬ ВИКОРИСТАННЯ ЯРГАРІВ НА КОМАНДОВІЙ ЛІНІЇ

Я не рекомендую цей приклад, який я знайшов повтореним в іншому місці. Це не вдасться для клавіш Unicode і неймовірно повільне для навіть помірної кількості клавіш:

redis-cli --raw keys "user:*"| xargs redis-cli del

У цьому прикладі xargs створює новий процес redis-cli для кожного ключа! це погано.

Я оцінив цей підхід у 4 рази повільніше, ніж перший приклад python, коли він видаляв кожен ключ по одному і в 20 разів повільніше, ніж видалення партіями по 500.


Я продовжую отримувати "redis.exceptions.ResponseError: невідома команда 'SCAN'" під час ітерації через r.scan_iter (). Будь-яка ідея чому? Я ще не знайшов відповіді.
BringBackCommodore64

1
@ BringBackCommodore64 Ваша версія redis застаріла, встановіть нову.
piokuc

@piokuc Ну, я не модернізував свій редіс, але ваша здогадка здається очевидно правильною!
BringBackCommodore64

@ BringBackCommodore64 Це не здогадка. У мене була та ж проблема, оновлення вирішило це. Не пам’ятаю версію, що у мене була, яка не підтримувала сканування, але їй було кілька років. Будь-яка остання версія Redis повинна бути в порядку.
piokuc

1
Пошук у Redis @LeiYang дозволяє глобуси / символи підстановки. Отже "mykey *", "user_ ", "user: ". redis.io/commands/keys
Патрік Коллінз

57

Так, використовуйте keys()з модуля StrictRedis:

>>> import redis
>>> r = redis.StrictRedis(host=YOUR_HOST, port=YOUR_PORT, db=YOUR_DB)
>>> r.keys()

Якщо вказати нульовий шаблон, ви отримаєте їх усі. За посиланням на сторінку:

клавіші (шаблон = '*')

Повертає список ключів, що відповідають шаблону


14
Майте на увазі, що використання цієї команди не рекомендується на виробничих серверах. Якщо у вас велика кількість ключів, ваш примірник Redis не відповість на будь-який інший запит під час обробки цього, що може зайняти досить тривалий час.
Паскаль Ле Меррер

2
Подумайте про додавання посилання на SCANкоманду, оскільки це зараз кращий спосіб отримати всі ключі з O (1) часовою складністю кожного запиту. (та O (N) для всіх запитів)
Кирило Зайцев

2
r.keys()є досить повільним, коли ви намагаєтеся зіставити шаблон, а не просто повертаєте всі ключі. Подумайте про використання, scanяк пропонується у відповіді нижче
cnikolaou

2
@KonstantineNikolaou Я повідомив ОП, і він із задоволенням прийняв мою відповідь прийняти іншу. Дякую за повідомлення, я давно цим користувався, але зараз мені не вистачає уваги на тему, щоб перевірити, що найкраще.
Fedorqui 'SO prestani шкодити'

@fedorqui рада почути це 😊
cnikolaou

11
import redis
r = redis.Redis("localhost", 6379)
for key in r.scan_iter():
       print key

за допомогою бібліотеки Pyredis

команда сканування

Доступно з 2.8.0.

Складність часу: O (1) для кожного дзвінка. O (N) для повної ітерації, включаючи достатню кількість викликів команд, щоб курсор повернувся до 0. N - кількість елементів всередині колекції.


0

Я хотів би додати приклад коду для відповіді Патріка та інші.
Це показує результати як за допомогою клавіш, так і методу scan_iter. І зверніть увагу, що Python3 використовує zip_longest замість izip_longest. Наведений нижче код переглядає всі клавіші та відображає їх. Я встановив розмір пакета як змінну 12, щоб зменшити результат.

Я написав це, щоб краще зрозуміти, як працювало групування ключів.

import redis
from itertools import zip_longest

\# connection/building of my redisObj omitted here

\# iterate a list in batches of size n
def batcher(iterable, n):
    args = [iter(iterable)] * n
    return zip_longest(*args)
    
result1 = redisObj.get("TestEN")
print(result1)
result2 = redisObj.get("TestES")
print(result2)

print("\n\nLoop through all keys:")
keys = redisObj.keys('*')
counter = 0
print("len(keys)=", len(keys))
for key in keys:
    counter +=1
    print (counter, "key=" +key, " value=" + redisObj.get(key))

print("\n\nLoop through all keys in batches (using itertools)")
\# in batches of 500 delete keys matching user:*
counter = 0
batch_counter = 0
print("Try scan_iter:")
for keybatch in batcher(redisObj.scan_iter('*'), 12):
    batch_counter +=1
    print(batch_counter, "keybatch=", keybatch)
    for key in keybatch:
        if key != None:
            counter += 1
            print("  ", counter, "key=" + key, " value=" + redisObj.get(key))

Приклад виводу:

Loop through all keys:
len(keys)= 2
1 key=TestES  value=Ola Mundo
2 key=TestEN  value=Hello World


Loop through all keys in batches (using itertools)
Try scan_iter:
1 keybatch= ('TestES', 'TestEN', None, None, None, None, None, None, None, None, None, None)
   1 key=TestES  value=Ola Mundo
   2 key=TestEN  value=Hello World

Зверніть увагу, що команди redis є однопотоковими, тому виконання клавіш () може заблокувати інші дії redis. Дивіться чудовий пост тут, який пояснює це більш докладно: SCAN проти KEYS продуктивності в Redis


0

Додаток до прийнятої відповіді вище.

scan_iter можна використовувати з count параметром для того, щоб сказати redis здійснювати пошук за кількістю ключів під час однієї ітерації. Це може значно пришвидшити отримання ключів, особливо при використанні з відповідним шаблоном та на великих клавішних просторах.

Будьте обережні, жорсткі, використовуючи дуже високі значення для підрахунку, оскільки це може зіпсувати продуктивність для інших паралельних запитів.

https://docs.keydb.dev/blog/2020/08/10/blog-post/ Ось стаття з додатковою інформацією та деякими орієнтирами.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.