Cómo almacenar y recuperar un diccionario con redis

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

¿Cómo almacenaría my_dict y lo recuperaría con redis? Por ejemplo, el siguiente código no funciona.

#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
fuente

Respuestas:

160

Puede hacerlo mediante hmset(se pueden configurar varias teclas usando 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
fuente
48
si se trata de una estructura de datos anidada en lugar de simplemente dictar, por ejemplo, que contiene algunas matrices, etc., serialice sus datos con json.dumps()escritura como cadena y después de recuperarlos del usuario de redis json.loads()para deserializarlos de nuevo a la estructura de datos de Python
andilabs
7
json.dumps()y json.loads()solo funcionará si está de acuerdo con que las claves de su diccionario sean siempre cadenas. Si no es así, entonces podría considerar usar pickle.
ryechus
6
json no es compatible con bytes, por lo que la serilización de json no es una solución global, por ejemplo, si tiene un dict con un valor de bytes, esto no funcionará.
Tommy
8
A modo de nota, la documentación de hmsetno le dice esto, pero genera un DataError si intenta almacenar un dict vacío.
hlongmore
1
@Pradeep cómo podemos hacer que la clave sea dinámica. Supongamos que los datos se insertan cada 15 minutos, entonces, ¿cómo puedo hacer que la clave sea dinámica
ak3191
36

puede encurtir su dictado y guardarlo como cadena.

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
fuente
11
Esto es cierto, pero dependiendo de la velocidad de lectura y escritura, esto puede agregar una sobrecarga importante. El decapado es una operación lenta
Tommy
1
Tenga en cuenta que si esto se usa con la entrada del usuario, su servidor es propenso a la ejecución remota de código , pickle.loadssolo debe usarse en datos 100% confiables
Paradoxis
16

Otra forma: puedes usar la RedisWorksbiblioteca.

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

Convierte tipos de Python en tipos de Redis y viceversa.

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

Descargo de responsabilidad: escribí la biblioteca. Aquí está el código: https://github.com/seperman/redisworks

Seperman
fuente
2
A modo de nota, RedisWorks usa hmsetbajo el capó si establece una variable en un dict, y por lo tanto, si lo hace root.something = {}, obtendrá un DataError, porque hmsetno permite diccionarios vacíos. Menciono esto porque la documentación de redis no te lo dice.
hlongmore
Interesante. Sí, lo usa hmset. Voy a mirar en esto. @hlongmore
Seperman
Pero aún así, ¿puede admitir bytes en el diccionario?
ZettaCircl
12

Como la respuesta básica ya la han dado otras personas, me gustaría agregarle algunas.

A continuación se muestran los comandos REDISpara realizar operaciones básicas con HashMap/Dictionary/Mappingvalores de tipo.

  1. HGET => Devuelve el valor de una sola clave pasada
  2. HSET => establecer / actualizar el valor de la clave única
  3. HMGET => Devuelve el valor para claves únicas / múltiples pasadas
  4. HMSET => establecer / actualizar valores para la clave múltiple
  5. HGETALL => Devuelve todos los pares (clave, valor) en el mapeo.

Los siguientes son sus métodos respectivos en la redis-pybiblioteca: -

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

Todos los métodos de establecimiento anteriores crean el mapeo, si no existe. Todos los métodos getter anteriores no generan errores / excepciones, si el mapeo / clave en el mapeo no existe.

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

Espero que aclare las cosas.

Mangu Singh Rajpurohit
fuente
cómo se puede hacer que la clave sea dinámica
ak3191
11

Si desea almacenar un diccionario de Python en redis, es mejor almacenarlo como una cadena 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)

Al recuperar la deserialización usando json.loads

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

¿Qué pasa con los tipos (por ejemplo, bytes) que no están serializados por funciones json?

Puede escribir funciones de codificador / decodificador para tipos que las funciones json no pueden serializar. p.ej. escritura de la función codificador / decodificador base64 / ascii para matriz de bytes.

Saji Xavier
fuente
Bajé la votación porque algunos dictados no se pueden serializar a JSON, por ejemplo, dictados con un valor de bytes.
Tommy
1
Puede escribir una función de codificador / decodificador (según el requisito, por ejemplo, codificación base64 / ascii) para los tipos que no se pueden codificar / decodificar de forma predeterminada.
Saji Xavier
@Tommy: incluso si usa hmset / hgetall, es posible que deba codificar / decodificar tipos que no son compatibles con redis.
Saji Xavier
1
No estar de acuerdo sobre "... la última operación es O (N)". N es el número de campos que tiene un enlace a la clave. Hacer N SET / GET o 1 HGET / HSET tiene la misma complejidad. Ver: redis.io/commands/hmset En cuanto al tiempo, HGET / HSET son transacciones atómicas y, por lo tanto, REDIS las realiza más rápido. Simplemente está moviendo la complejidad de Redis a Python Code.
ZettaCircl
La ventaja de hmset es la posibilidad de recuperar solo ciertas subpartes del dict. Con json perdemos eso, así que esto es tan bueno como pickle u otra cosa.
Jorge Leitao
5

Se podría considerar el uso de MessagePack, que está respaldado por 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]}

Usando msgpack-python y aioredis

Ohad Lahav
fuente
4

De otra forma puedes abordar el asunto:

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!

No lo probé por eficiencia / velocidad.

roberto
fuente
3

El comando redis SET almacena una cadena, no datos arbitrarios. Puede intentar usar el comando redis HSET para almacenar el dict como un hash de redis con algo como

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

pero los tipos de datos redis y los tipos de datos python no se alinean del todo. Los dictados de Python se pueden anidar arbitrariamente, pero un hash de redis requerirá que su valor sea una cadena. Otro enfoque que puede tomar es convertir sus datos de Python en cadenas y almacenarlos en redis, algo como

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

y luego, cuando saque la cadena, deberá analizarla para recrear el objeto de Python.

Jesusaurio
fuente
1
puede convertir sus datos a json y almacenar el resultado en redis
Narcisse Doudieu Siewe
3

HMSET está obsoleto. Ahora puede usar HSET con un diccionario de la siguiente manera:

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
fuente
¡Gracias! Estoy tratando de encontrar el documento donde se explica todo esto. Sabes donde está. Por ejemplo, ¿para qué sirven los dos "Nones"?
NealWalters
@NealWalters: Vea la línea en la página de comandos de HMSET - redis.io/commands/hmset para ver la advertencia de obsolescencia .
Saransh Singh
0

Prueba rejson-py, que es relativamente nuevo desde 2017. Mira esta introducción .

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
fuente
0

Si no sabe exactamente cómo organizar los datos en Redis, hice algunas pruebas de rendimiento, incluido el análisis de resultados. El diccionario que usé ( d ) tenía 437.084 claves (formato md5) y los valores de esta forma:

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

Primera prueba (insertando datos en una asignación de valor-clave de 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

Segunda prueba (insertando datos directamente en las claves de 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

Como puede ver, en la segunda prueba, solo se deben analizar los valores de 'información', porque hgetall (clave) ya devuelve un dict, pero no uno anidado.

Y, por supuesto, el mejor ejemplo de cómo usar Redis como dictados de Python es la Primera Prueba

Tavy
fuente