Як зберігати та отримувати словник за допомогою redis


93
# I have the dictionary my_dict
my_dict = {
    'var1' : 5
    'var2' : 9
}
r = redis.StrictRedis()

Як би я зберігав my_dict і отримував його за допомогою redis. Наприклад, наступний код не працює.

#Code that doesn't work
r.set('this_dict', my_dict)  # to store my_dict in this_dict
r.get('this_dict')  # to retrieve my_dict

Відповіді:


160

Ви можете зробити це за допомогою hmset(кілька клавіш можна встановити за допомогою hmset).

hmset("RedisKey", dictionaryToSet)

import redis
conn = redis.Redis('localhost')

user = {"Name":"Pradeep", "Company":"SCTL", "Address":"Mumbai", "Location":"RCP"}

conn.hmset("pythonDict", user)

conn.hgetall("pythonDict")

{'Company': 'SCTL', 'Address': 'Mumbai', 'Location': 'RCP', 'Name': 'Pradeep'}

48
якщо це вкладена структура даних, а не просто dict, наприклад, що містить деякі масиви тощо, серіалізуйте ваші дані, записуючи json.dumps()як рядок, і після отримання від користувача json.loads()redis для десеріалізації їх назад до структури даних python
andilabs

7
json.dumps()і json.loads()працюватиме лише в тому випадку, якщо з вами все добре, якщо ваші ключі словника завжди є рядками. Якщо ви цього не зробите, ви можете розглянути можливість використання соління.
ryechus

6
json не сумісний з байтами, тому серилізація json не є глобальним рішенням, наприклад, якщо у вас є dict зі значенням байт, це не спрацює.
Томмі

8
Зауважимо, що документація для hmsetцього не повідомляє, але це викликає помилку DataError, якщо ви намагаєтеся зберегти порожній dict.
hlongmore

1
@Pradeep, як ми можемо зробити ключ динамічним. припустимо, дані вставляються кожні 15 хвилин, щоб я міг зробити ключ динамічним
ak3191

36

Ви можете замаринувати свій дикт і зберегти як рядок.

import pickle
import redis

r = redis.StrictRedis('localhost')
mydict = {1:2,2:3,3:4}
p_mydict = pickle.dumps(mydict)
r.set('mydict',p_mydict)

read_dict = r.get('mydict')
yourdict = pickle.loads(read_dict)

11
Це правда, але залежно від швидкості читання та запису це може додати серйозних накладних витрат. Соління - це повільна операція
Томмі,

1
Зверніть увагу, що якщо це використовується з користувацьким введенням, ваш сервер схильний до віддаленого вилучення коду , pickle.loadsйого слід використовувати лише на 100% надійних даних
Парадоксис,

16

Інший спосіб: ви можете використовувати RedisWorksбібліотеку.

pip install redisworks

>>> from redisworks import Root
>>> root = Root()
>>> root.something = {1:"a", "b": {2: 2}}  # saves it as Hash type in Redis
...
>>> print(root.something)  # loads it from Redis
{'b': {2: 2}, 1: 'a'}
>>> root.something['b'][2]
2

Він перетворює типи python на типи Redis і навпаки.

>>> root.sides = [10, [1, 2]]  # saves it as list in Redis.
>>> print(root.sides)  # loads it from Redis
[10, [1, 2]]
>>> type(root.sides[1])
<class 'list'>

Застереження: Я написав бібліотеку. Ось код: https://github.com/seperman/redisworks


2
Зазначимо, RedisWorks використовує hmsetпід капотом, якщо ви встановите змінну як dict, і, отже, якщо ви це зробите, root.something = {}ви отримаєте DataError, оскільки hmsetне допускає порожніх словників. Я згадую це, оскільки документація для redis не говорить вам цього.
hlongmore

Цікаво. Так, він використовує hmset. Я розгляну це. @hlongmore
Сеперман,

Але все ж, чи може він підтримувати байти в словнику?
ZettaCircl

12

Оскільки основну відповідь вже дали інші люди, я хотів би додати до неї деякі.

Нижче наведено команди REDISдля виконання основних операцій зі HashMap/Dictionary/Mappingзначеннями типів.

  1. HGET => Повертає значення для переданого одного ключа
  2. HSET => встановити / оновити значення для одного ключа
  3. HMGET => Повертає значення для переданого одного / декількох ключів
  4. HMSET => встановити / оновити значення для множинного ключа
  5. HGETALL => Повертає всі пари (ключ, значення) у відображенні.

Нижче наведені їх відповідні методи в redis-pyбібліотеці: -

  1. HGET => hget
  2. HSET => hset
  3. HMGET => hmget
  4. HMSET => hmset
  5. HGETALL => hgetall

Усі вищезазначені методи сеттера створюють відображення, якщо воно не існує. Усі вищезазначені методи отримання не викликають помилок / винятків, якщо mapping / ключ у mapping не існує.

Example:
=======
In [98]: import redis
In [99]: conn = redis.Redis('localhost')

In [100]: user = {"Name":"Pradeep", "Company":"SCTL", "Address":"Mumbai", "Location":"RCP"}

In [101]: con.hmset("pythonDict", {"Location": "Ahmedabad"})
Out[101]: True

In [102]: con.hgetall("pythonDict")
Out[102]:
{b'Address': b'Mumbai',
 b'Company': b'SCTL',
 b'Last Name': b'Rajpurohit',
 b'Location': b'Ahmedabad',
 b'Name': b'Mangu Singh'}

In [103]: con.hmset("pythonDict", {"Location": "Ahmedabad", "Company": ["A/C Pri
     ...: sm", "ECW", "Musikaar"]})
Out[103]: True

In [104]: con.hgetall("pythonDict")
Out[104]:
{b'Address': b'Mumbai',
 b'Company': b"['A/C Prism', 'ECW', 'Musikaar']",
 b'Last Name': b'Rajpurohit',
 b'Location': b'Ahmedabad',
 b'Name': b'Mangu Singh'}

In [105]: con.hget("pythonDict", "Name")
Out[105]: b'Mangu Singh'

In [106]: con.hmget("pythonDict", "Name", "Location")
Out[106]: [b'Mangu Singh', b'Ahmedabad']

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


як можна зробити ключовий динамічний
ak3191

11

Якщо ви хочете зберегти python dict в redis, краще зберігати його як рядок json.

import redis

r = redis.StrictRedis(host='localhost', port=6379, db=0)
mydict = { 'var1' : 5, 'var2' : 9, 'var3': [1, 5, 9] }
rval = json.dumps(mydict)
r.set('key1', rval)

Під час отримання десеріалізуйте його за допомогою json.loads

data = r.get('key1')
result = json.loads(data)
arr = result['var3']

Що можна сказати про типи (наприклад, байти), які не серіалізовані функціями json?

Ви можете писати функції кодера / декодера для типів, які не можуть бути серіалізовані функціями json. напр. запис функції кодування / декодера base64 / ascii для байтового масиву.


Я підтримав це, оскільки деякі дикти не можуть бути серіалізовані в JSON, наприклад, дикти зі значенням байт.
Томмі

1
Ви можете написати функцію кодера / декодера (відповідно до вимоги, наприклад, кодування base64 / ascii) для типів, які неможливо кодувати / декодувати за замовчуванням.
Саджі Ксав'єр

@Tommy - навіть якщо використовується hmset / hgetall, можливо, вам доведеться кодувати / декодувати типи, які не підтримуються redis.
Саджі Ксав'єр

1
Не погоджуючись щодо "... остання операція - O (N)." N - кількість полів, у яких є посилання на ключ. Виконання N SET / GET або 1 HGET / HSET - однакова складність. Дивіться: redis.io/commands/hmset З часом, HGET / HSET є атомними транзакціями, тому REDIS виконує їх швидше. Ви просто переносите складність з Redis на Python Code.
ZettaCircl

Перевагою hmset є можливість отримати лише певні підрозділи вироку. З json ми втрачаємо це, тож це так само добре, як соління або інше.
Хорхе

5

Можна розглянути можливість використання MessagePack, який схвалений Redis.

import msgpack

data = {
    'one': 'one',
    'two': 2,
    'three': [1, 2, 3]
}

await redis.set('my-key', msgpack.packb(data))
val = await redis.get('my-key')
print(msgpack.unpackb(val))

# {'one': 'one', 'two': 2, 'three': [1, 2, 3]}

Використання msgpack-python та aioredis


4

Інший спосіб ви можете підійти до цього питання:

import redis
conn = redis.Redis('localhost')

v={'class':'user','grants': 0, 'nome': 'Roberto', 'cognome': 'Brunialti'}

y=str(v)
print(y['nome']) #<=== this return an error as y is actually a string
conn.set('test',y)

z=eval(conn.get('test'))
print(z['nome']) #<=== this really works!

Я не перевіряв його на ефективність / швидкість.


3

Команда redis SET зберігає рядок, а не довільні дані. Ви можете спробувати використати команду redis HSET для зберігання дикту як хешу redis з чимось подібним

for k,v in my_dict.iteritems():
    r.hset('my_dict', k, v)

але типи даних redis та типи даних python не зовсім узгоджуються. Диктовки Python можуть бути довільно вкладені, але хеш redis вимагатиме, щоб ваше значення було рядком. Інший підхід, який ви можете застосувати, - це перетворити ваші дані python на рядок і зберегти їх у redis, приблизно так

r.set('this_dict', str(my_dict))

а потім, коли ви отримаєте рядок, вам потрібно буде проаналізувати його, щоб відтворити об’єкт python.


1
він може перетворити свої дані на json і зберегти результат у redis
Narcisse Doudieu Siewe

3

HMSET застарілий. Тепер ви можете використовувати HSET зі словником наступним чином:

import redis
r = redis.Redis('localhost')

key = "hashexample" 
queue_entry = { 
    "version":"1.2.3", 
    "tag":"main", 
    "status":"CREATED",  
    "timeout":"30"
    }
r.hset(key,None,None,queue_entry)

Дякую! Я намагаюся знайти документ, де все це прописано. Чи знаєте ви, де це. Наприклад, для чого потрібні два "нічого".
NealWalters,

@NealWalters: Див. Рядок на командній сторінці HMSET - redis.io/commands/hmset для попередження про припинення використання.
Саранш Сінгх,

0

Спробуйте rejson-py, який є відносно новим з 2017 року. Подивіться на це вступ .

from rejson import Client, Path

rj = Client(host='localhost', port=6379)

# Set the key `obj` to some object
obj = {
    'answer': 42,
    'arr': [None, True, 3.14],
    'truth': {
        'coord': 'out there'
    }
}
rj.jsonset('obj', Path.rootPath(), obj)

# Get something
print 'Is there anybody... {}?'.format(
    rj.jsonget('obj', Path('.truth.coord'))
)

# Delete something (or perhaps nothing), append something and pop it
rj.jsondel('obj', Path('.arr[0]'))
rj.jsonarrappend('obj', Path('.arr'), 'something')
print '{} popped!'.format(rj.jsonarrpop('obj', Path('.arr')))

# Update something else
rj.jsonset('obj', Path('.answer'), 2.17)

0

Якщо ви не знаєте точно, як організувати дані в Redis, я провів кілька тестів продуктивності, включаючи аналіз результатів. Словник, яким я користувався ( d ), містив 437.084 ключі (формат md5), і значення цієї форми:

{"path": "G:\tests\2687.3575.json",
 "info": {"f": "foo", "b": "bar"},
 "score": 2.5}

Перший тест (вставка даних у відображення ключа-значення redis):

conn.hmset('my_dict', d)  # 437.084 keys added in 8.98s

conn.info()['used_memory_human']  # 166.94 Mb

for key in d:
    json.loads(conn.hget('my_dict', key).decode('utf-8').replace("'", '"'))
    #  41.1 s

import ast
for key in d:
    ast.literal_eval(conn.hget('my_dict', key).decode('utf-8'))
    #  1min 3s

conn.delete('my_dict')  # 526 ms

Другий тест (вставка даних безпосередньо в клавіші Redis):

for key in d:
    conn.hmset(key, d[key])  # 437.084 keys added in 1min 20s

conn.info()['used_memory_human']  # 326.22 Mb

for key in d:
    json.loads(conn.hgetall(key)[b'info'].decode('utf-8').replace("'", '"'))
    #  1min 11s

for key in d:
    conn.delete(key)
    #  37.3s

Як бачите, у другому тесті потрібно проаналізувати лише значення "інформація", оскільки hgetall (ключ) вже повертає dict, але не вкладений.

І звичайно, найкращим прикладом використання Redis як диктовки python є Перший тест

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