Как сохранить и получить словарь с помощью 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
PiccolMan
источник

Ответы:

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'}
PradeepK
источник
48
если это вложенная структура данных, а не просто dict, например, содержащая некоторые массивы и т. д., сериализируйте свои данные с помощью json.dumps()записи в виде строки и после получения от пользователя redis json.loads()для десериализации обратно в структуру данных python
andilabs
7
json.dumps()и json.loads()будет работать только в том случае, если вас устраивает то, что ключи словаря всегда являются строками. В противном случае вы можете рассмотреть возможность использования рассола.
ryechus
6
json несовместим с байтами, поэтому сериализация json не является глобальным решением, например, если у вас есть dict со значением байтов, это не сработает.
Tommy
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)
DaveQ
источник
11
Это правда, но в зависимости от скорости чтения и записи это может добавить серьезные накладные расходы. Травление - медленная операция
Томми
1
Обратите внимание, что если это используется с пользовательским вводом, ваш сервер подвержен удаленному запуску кода , pickle.loadsего следует использовать только для 100% надежных данных
Paradoxis
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
Seperman
Но все же может ли он поддерживать байты в словаре?
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

Все вышеперечисленные методы установки создают отображение, если оно не существует. Все вышеперечисленные методы получения не вызывают ошибок / исключений, если сопоставление / ключ в сопоставлении не существует.

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, например, слова со значением в байтах.
Tommy
1
Вы можете написать функцию кодировщика / декодера (в соответствии с требованиями, например, кодирование base64 / ascii) для типов, которые не могут быть закодированы / декодированы по умолчанию.
Саджи Ксавье
@Tommy - даже если вы используете hmset / hgetall, вам может потребоваться кодировать / декодировать типы, которые не поддерживаются redis.
Saji Xavier 02
1
Несогласие с «... последняя операция O (N)». N - количество полей, у которых есть ссылка на ключ. Выполнение N SET / GET или 1 HGET / HSET - такая же сложность. См .: redis.io/commands/hmset По времени HGET / HSET являются атомарными транзакциями, поэтому REDIS выполняет их быстрее. Вы просто переносите сложность с Redis на код Python.
ZettaCircl
Преимущество hmset заключается в возможности извлекать только определенные части dict. С json мы теряем это, так что это не хуже, чем pickle или что-то еще.
Хорхе Лейтао
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, чтобы сохранить dict как хеш redis с чем-то вроде

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

но типы данных redis и типы данных python не совсем совпадают. Python dicts может быть произвольно вложенным, но хэш Redis требует, чтобы ваше значение было строкой. Другой подход, который вы можете использовать, - это преобразовать данные Python в строку и сохранить ее в Redis, например

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

а затем, когда вы получите строку, вам нужно будет проанализировать ее, чтобы воссоздать объект python.

Иисусозавр
источник
1
он может преобразовать свои данные в json и сохранить результат в redis
Нарцисс Дудье Сью
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

Как видите, во втором тесте нужно анализировать только значения info, потому что hgetall (key) уже возвращает dict, но не вложенный.

И, конечно же, лучший пример использования Redis в качестве dicts Python - это First Test.

Тави
источник