Jak przechowywać i pobierać słownik za pomocą redis

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

Jak przechowywać my_dict i odzyskać go za pomocą redis. Na przykład poniższy kod nie działa.

#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
źródło

Odpowiedzi:

160

Możesz to zrobić za pomocą hmset(wiele kluczy można ustawić za pomocą 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
źródło
48
jeśli jest to zagnieżdżona struktura danych, a nie po prostu dyktowanie, np. zawiera jakieś tablice itp. serialzie twoje dane za pomocą json.dumps()zapisu jako łańcuch i po odzyskaniu od użytkownika redis w json.loads()celu deserializacji z powrotem do struktury danych Pythona
andilabs
7
json.dumps()i json.loads()będzie działać tylko wtedy, gdy nie przeszkadza ci, że klucze słownika zawsze są łańcuchami. Jeśli nie, możesz rozważyć użycie marynaty.
ryechus
6
json nie jest kompatybilny z bajtami, więc serilizacja json nie jest rozwiązaniem globalnym, np. jeśli masz dykt z wartością bajtów, to nie zadziała.
Tommy
8
Nawiasem mówiąc, dokumentacja dla hmsetnie mówi tego, ale wywołuje błąd DataError, jeśli spróbujesz zapisać pusty dykt.
hlongmore
1
@Pradeep, jak możemy uczynić klucz dynamicznym. przypuśćmy, że dane są wstawiane co 15 minut, więc jak mogę uczynić klucz dynamicznym
ak3191
36

możesz marynować swój dykt i zapisać jako ciąg.

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
źródło
11
To prawda, ale w zależności od szybkości odczytów i zapisów może to spowodować poważne obciążenie. Marynowanie to powolna operacja
Tommy
1
Należy pamiętać, że jeśli jest to używane przy wprowadzaniu danych przez użytkownika, serwer jest podatny na zdalne wykrywanie kodu , pickle.loadspowinien być używany tylko w przypadku danych w 100% zaufanych
Paradoxis
16

Inny sposób: możesz skorzystać z RedisWorksbiblioteki.

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

Konwertuje typy Pythona na typy Redis i odwrotnie.

>>> 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'>

Zastrzeżenie: napisałem bibliotekę. Oto kod: https://github.com/seperman/redisworks

Seperman
źródło
2
Nawiasem mówiąc, RedisWorks używa hmsetpod maską, jeśli ustawisz zmienną na dict, a więc jeśli to zrobisz root.something = {}, otrzymasz DataError, ponieważ hmsetnie zezwala na puste słowniki. Wspominam o tym, ponieważ dokumentacja dla redis nie mówi tego.
hlongmore
Ciekawy. Tak, używa hmset. Przyjrzę się temu. @hlongmore
Seperman
Ale czy nadal może obsługiwać bajty w słowniku?
ZettaCircl
12

Ponieważ podstawowa odpowiedź została już udzielona przez innych ludzi, chciałbym dodać do niej trochę.

Poniżej znajdują się polecenia programu REDISsłużące do wykonywania podstawowych operacji na HashMap/Dictionary/Mappingwartościach typu.

  1. HGET => Zwraca wartość dla pojedynczego przekazanego klucza
  2. HSET => ustawia / aktualizuje wartość dla pojedynczego klucza
  3. HMGET => Zwraca wartość dla jednego / wielu przekazanych kluczy
  4. HMSET => ustaw / aktualizuje wartości dla klucza wielokrotnego
  5. HGETALL => Zwraca wszystkie pary (klucz, wartość) w mapowaniu.

Oto ich odpowiednie metody w redis-pybibliotece: -

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

Wszystkie powyższe metody ustawiające tworzą mapowanie, jeśli nie istnieje. Wszystkie powyższe metody pobierające nie generują błędów / wyjątków, jeśli mapowanie / klucz w mapowaniu nie istnieje.

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']

Mam nadzieję, że to wyjaśnia sprawę.

Mangu Singh Rajpurohit
źródło
jak sprawić, by klucz był dynamiczny
ak3191
11

Jeśli chcesz przechowywać dyktando Pythona w redis, lepiej zapisać go jako ciąg 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)

Podczas pobierania de-serializacji przy użyciu json.loads

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

A co z typami (np. Bajty), które nie są serializowane przez funkcje json?

Można pisać funkcje kodera / dekodera dla typów, których nie można serializować za pomocą funkcji JSON. na przykład. zapisywanie funkcji kodera / dekodera base64 / ascii dla tablicy bajtów.

Saji Xavier
źródło
Odrzuciłem to, ponieważ niektórych dykt nie można serializować do JSON, na przykład dykt z wartością bajtów.
Tommy
1
Możesz napisać funkcję kodera / dekodera (zgodnie z wymaganiami, np. Kodowanie base64 / ascii) dla typów, których domyślnie nie można zakodować / zdekodować.
Saji Xavier
@Tommy - nawet jeśli używasz hmset / hgetall, może być konieczne zakodowanie / zdekodowanie typów, które nie są obsługiwane przez redis.
Saji Xavier
1
Nie zgadzam się na temat „... ostatnią operacją jest O (N)”. N to liczba pól, do których masz link do klucza. Wykonanie N SET / GET lub 1 HGET / HSET to taka sama złożoność. Zobacz: redis.io/commands/hmset Czasowo, HGET / HSET są transakcjami atomowymi, a więc są wykonywane szybciej przez REDIS. Po prostu przenosisz złożoność z Redis do Python Code.
ZettaCircl
Zaletą hmset jest możliwość pobrania tylko niektórych części dyktatu. W przypadku json tracimy to, więc jest to równie dobre jak pikle lub coś innego.
Jorge Leitao
5

Można rozważyć użycie MessagePack, który jest wspierany przez 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]}

Korzystanie z msgpack-python i aioredis

Ohad Lahav
źródło
4

Inny sposób podejścia do sprawy:

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!

Nie testowałem tego pod kątem wydajności / szybkości.

roberto
źródło
3

Polecenie redis SET przechowuje ciąg, a nie dowolne dane. Możesz spróbować użyć polecenia redis HSET, aby zapisać dict jako skrót redis z czymś w rodzaju

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

ale typy danych redis i python nie są do siebie dopasowane. Wskazówki Pythona mogą być dowolnie zagnieżdżane, ale skrót redis będzie wymagał, aby twoja wartość była ciągiem. Innym podejściem, które możesz zastosować, jest przekonwertowanie danych Pythona na łańcuchy i przechowywanie ich w redis, na przykład

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

a potem, gdy otrzymasz napis, będziesz musiał go przeanalizować, aby odtworzyć obiekt Pythona.

Jesusaur
źródło
1
może przekonwertować swoje dane na json i zapisać wynik w redis
Narcisse Doudieu Siewe
3

HMSET jest przestarzały. Możesz teraz używać HSET ze słownikiem w następujący sposób:

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)
Tad Guski
źródło
Dzięki! Próbuję znaleźć doktora, w którym to wszystko jest zapisane. Czy wiesz gdzie to jest. Na przykład, do czego służą dwa „brak”.
NealWalters
@NealWalters: Zobacz wiersz na stronie poleceń HMSET - redis.io/commands/hmset, aby uzyskać ostrzeżenie o wycofaniu.
Saransh Singh
0

Wypróbuj rejson-py, który jest stosunkowo nowy od 2017 roku. Spójrz na to wprowadzenie .

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)
Kevin Zhu
źródło
0

Jeśli nie wiesz dokładnie, jak uporządkować dane w Redis, zrobiłem kilka testów wydajnościowych, w tym analizę wyników. Dictonary, których użyłem ( d ), zawierało 437,084 kluczy (format md5) i wartości tej postaci:

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

Pierwszy test (wstawianie danych do mapowania klucz-wartość 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

Drugi test (wstawianie danych bezpośrednio do kluczy 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

Jak widać, w drugim teście trzeba przeanalizować tylko wartości „info”, ponieważ hgetall (klucz) już zwraca dict, ale nie zagnieżdżony.

I oczywiście najlepszym przykładem użycia Redisa jako nakazu Pythona jest pierwszy test

Tavy
źródło