Comment stocker et récupérer un dictionnaire avec Redis

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

Comment pourrais-je stocker mon_dict et le récupérer avec redis. Par exemple, le code suivant ne fonctionne pas.

#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
la source

Réponses:

160

Vous pouvez le faire par hmset(plusieurs touches peuvent être définies à l'aide de 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
la source
48
si elle est une structure de données imbriquées plutôt que de simplement dict, par exemple contenant certains tableaux , etc. serialzie vos données avec json.dumps()écriture en tant que chaîne et après retrive de Redis utilisateur json.loads()pour désérialisation revenir à la structure de données python
andilabs
7
json.dumps()et json.loads()ne fonctionnera que si vos clés de dictionnaire sont toujours des chaînes. Si ce n'est pas le cas, vous pouvez envisager d'utiliser des cornichons.
ryechus
6
json n'est pas compatible avec les octets, donc la sérilisation json n'est pas une solution globale, par exemple, si vous avez un dict avec une valeur d'octets, cela ne fonctionnera pas.
Tommy
8
À titre de note, la documentation de hmsetne vous le dit pas, mais elle déclenche une DataError si vous essayez de stocker un dict vide.
hlongmore
1
@Pradeep comment nous pouvons rendre la clé dynamique. supposons que les données soient insérées toutes les 15 minutes afin de savoir comment rendre la clé dynamique
ak3191
36

vous pouvez décaper votre dict et l'enregistrer sous forme de chaîne.

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
la source
11
C'est vrai, mais en fonction du taux de lectures et d'écritures, cela peut ajouter une surcharge importante. Le décapage est une opération lente
Tommy
1
Veuillez noter que si cela est utilisé avec l'entrée de l'utilisateur, votre serveur est sujet à l'exection de code à distance , pickle.loadsil ne doit être utilisé que sur des données de confiance à 100%
Paradoxis le
16

Une autre façon: vous pouvez utiliser la RedisWorksbibliothèque.

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

Il convertit les types python en types Redis et vice-versa.

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

Avertissement: j'ai écrit la bibliothèque. Voici le code: https://github.com/seperman/redisworks

Seperman
la source
2
À titre de note, RedisWorks utilise hmsetsous le capot si vous définissez une variable sur un dict, et donc si vous le faites, root.something = {}vous obtiendrez un DataError, car hmsetn'autorise pas les dictionnaires vides. Je mentionne cela parce que la documentation de redis ne vous le dit pas.
hlongmore
Intéressant. Oui, il utilise hmset. J'examinerai ceci. @hlongmore
Seperman
Mais quand même, peut-il prendre en charge les octets dans le dictionnaire?
ZettaCircl
12

Comme la réponse de base a déjà été donnée par d'autres personnes, je voudrais en ajouter quelques-unes.

Voici les commandes REDISpour effectuer des opérations de base avec HashMap/Dictionary/Mappingdes valeurs de type.

  1. HGET => Renvoie la valeur pour une seule clé passée
  2. HSET => définir / mettre à jour la valeur de la clé unique
  3. HMGET => Renvoie la valeur des clés simples / multiples passées
  4. HMSET => définir / mettre à jour les valeurs de la clé multiple
  5. HGETALL => Renvoie toutes les paires (clé, valeur) du mappage.

Voici leurs méthodes respectives dans la redis-pybibliothèque: -

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

Toutes les méthodes setter ci-dessus créent le mappage, s'il n'existe pas. Toutes les méthodes getter ci-dessus ne déclenchent pas d'erreur / d'exceptions, si le mappage / clé dans le mappage n'existe pas.

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

J'espère que cela rend les choses plus claires.

Mangu Singh Rajpurohit
la source
comment vous pouvez rendre la clé dynamique
ak3191
11

Si vous souhaitez stocker un dict python dans redis, il est préférable de le stocker en tant que chaîne 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)

Lors de la récupération de la désérialisation, utilisez json.loads

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

Qu'en est-il des types (par exemple, octets) qui ne sont pas sérialisés par les fonctions json?

Vous pouvez écrire des fonctions d'encodeur / décodeur pour des types qui ne peuvent pas être sérialisés par des fonctions json. par exemple. écriture de la fonction encodeur / décodeur base64 / ascii pour un tableau d'octets.

Saji Xavier
la source
J'ai voté à la baisse car certains dictionnaires ne peuvent pas être sérialisés en JSON, par exemple, les dictionnaires avec une valeur d'octets.
Tommy
1
Vous pouvez écrire une fonction d'encodeur / décodeur (selon l'exigence, par exemple l'encodage base64 / ascii) pour les types qui ne peuvent pas être encodés / décodés par défaut.
Saji Xavier
@Tommy - même si vous utilisez hmset / hgetall, vous devrez peut-être encoder / décoder des types qui ne sont pas pris en charge par redis.
Saji Xavier
1
Pas d'accord sur "... la dernière opération est O (N)". N étant le nombre de champs que vous avez liés à la clé. Faire N SET / GET ou 1 HGET / HSET est la même complexité. Voir: redis.io/commands/hmset Dans le temps, HGET / HSET sont des transactions atomiques, et sont donc exécutés plus rapidement par REDIS. Vous déplacez simplement la complexité de Redis vers Python Code.
ZettaCircl
L'avantage de hmset est la possibilité de ne récupérer que certaines sous-parties du dict. Avec json, nous perdons cela, donc c'est aussi bon que du cornichon ou autre chose.
Jorge Leitao
5

On pourrait envisager d'utiliser MessagePack qui est approuvé par 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]}

Utilisation de msgpack-python et aioredis

Ohad Lahav
la source
4

Une autre façon d'aborder la question:

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!

Je ne l'ai pas testé pour l'efficacité / la vitesse.

Roberto
la source
3

La commande redis SET stocke une chaîne, pas des données arbitraires. Vous pouvez essayer d'utiliser la commande redis HSET pour stocker le dict sous forme de hachage redis avec quelque chose comme

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

mais les types de données redis et les types de données python ne s'alignent pas tout à fait. Les dictées Python peuvent être imbriquées arbitrairement, mais un hachage redis exigera que votre valeur soit une chaîne. Une autre approche que vous pouvez adopter est de convertir vos données python en chaîne et de les stocker dans redis, quelque chose comme

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

puis lorsque vous sortez la chaîne, vous devrez l'analyser pour recréer l'objet python.

Jesusaur
la source
1
il peut convertir ses données en json et stocker le résultat dans redis
Narcisse Doudieu Siewe
3

HMSET est obsolète. Vous pouvez maintenant utiliser HSET avec un dictionnaire comme suit:

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
la source
Merci! J'essaie de trouver le doc où tout cela est précisé. Est-ce que tu sais où c'est. Par exemple, à quoi servent les deux "Nones".
NealWalters le
@NealWalters: Consultez la ligne sur la page de commande HMSET - redis.io/commands/hmset pour un avertissement de dépréciation.
Saransh Singh
0

Essayez rejson-py qui est relativement nouveau depuis 2017. Regardez cette introduction .

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
la source
0

Si vous ne savez pas exactement comment organiser les données dans Redis, j'ai effectué des tests de performances, y compris l'analyse des résultats. Le dictonaire que j'ai utilisé ( d ) avait 437.084 clés (format md5), et les valeurs de cette forme:

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

Premier test (insertion de données dans un mappage clé-valeur 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

Deuxième test (insertion de données directement dans les clés 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

Comme vous pouvez le voir, dans le deuxième test, seules les valeurs «info» doivent être analysées, car le hgetall (clé) renvoie déjà un dict, mais pas un imbriqué.

Et bien sûr, le meilleur exemple d'utilisation de Redis comme dicton de python est le premier test

Tavy
la source