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

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

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)

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


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ę.


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.


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


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.


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.


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)

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)

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

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.