Como armazenar e recuperar um dicionário com o redis

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

Como eu armazenaria my_dict e o recuperaria com o redis. Por exemplo, o código a seguir não 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
fonte

Respostas:

166

Você pode fazer isso hmset(várias teclas podem ser definidas 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
fonte
51
se for uma estrutura de dados aninhada ao invés de simplesmente dict, por exemplo, contendo alguns arrays etc. serialzie seus dados com json.dumps()write as string e depois recupere do usuário redis json.loads()para desserializar de volta para a estrutura de dados python
andilabs
8
json.dumps()e json.loads()só funcionará se você aceitar que as chaves do seu dicionário sejam sempre strings. Se não, então você pode considerar o uso de picles.
Ryechus de
7
json não é compatível com bytes, então a serilização json não é uma solução global, por exemplo, se você tiver um dict com um valor de bytes, isso não funcionará.
Tommy
9
A título de observação, a documentação do hmsetnão informa isso, mas gera um DataError se você tentar armazenar um dicionário vazio.
hlongmore
2
@Pradeep como podemos tornar a chave dinâmica. suponha que os dados sejam inseridos a cada 15 minutos, então como posso tornar a chave dinâmica
ak3191,
37

você pode conservar seu dicionário e salvá-lo como barbante.

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
fonte
12
Isso é verdade, mas dependendo da taxa de leituras e gravações, isso pode adicionar sobrecarga séria. Decapagem é uma operação lenta
Tommy
2
Observe que se isso for usado com a entrada do usuário, seu servidor está sujeito à execução remota de código e pickle.loadssó deve ser usado em dados 100% confiáveis
Paradoxis
16

Outra forma: você pode usar a 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

Ele converte tipos python em tipos Redis e 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'>

Disclaimer: Eu escrevi a biblioteca. Aqui está o código: https://github.com/seperman/redisworks

Seperman
fonte
2
A título de observação, o RedisWorks usa " hmsetunder the capô" se você definir uma variável para um dict e, portanto, se você fizer root.something = {}isso, receberá um DataError, porque hmsetnão permite dicionários vazios. Menciono isso porque a documentação do redis não informa isso.
hlongmore
Interessante. Sim, usa hmset. Eu vou olhar para isso. @hlongmore
Seperman
Mesmo assim, ele pode suportar bytes no dicionário?
ZettaCircl
13

Como a resposta básica já foi dada por outras pessoas, gostaria de acrescentar algumas a ela.

A seguir estão os comandos REDISpara executar operações básicas com HashMap/Dictionary/Mappingvalores de tipo.

  1. HGET => Retorna valor para chave única passada
  2. HSET => definir / atualizar o valor para a chave única
  3. HMGET => Retorna o valor para chaves simples / múltiplas passadas
  4. HMSET => definir / atualizar valores para a chave múltipla
  5. HGETALL => Retorna todos os pares (chave, valor) no mapeamento.

A seguir estão seus respectivos métodos na redis-pybiblioteca: -

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

Todos os métodos setter acima criam o mapeamento, se ele não existir. Todos os métodos getter acima não levantam erros / exceções, se o mapeamento / chave no mapeamento não existir.

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 torne as coisas mais claras.

Mangu Singh Rajpurohit
fonte
como você pode tornar a chave dinâmica
ak3191
12

Se você deseja armazenar um dicionário python no redis, é melhor armazená-lo como string 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)

Ao recuperar, desserialize-o usando json.loads

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

E os tipos (por exemplo, bytes) que não são serializados por funções json?

Você pode escrever funções de codificador / decodificador para tipos que não podem ser serializados por funções json. por exemplo. escrever função de codificador / decodificador base64 / ascii para matriz de bytes.

Saji Xavier
fonte
Eu votei contra isso porque alguns dicts não podem ser serializados em JSON, por exemplo, dicts com um valor de bytes.
Tommy
1
Você pode escrever uma função de codificador / decodificador (de acordo com o requisito, por exemplo, codificação base64 / ascii) para os tipos que não podem ser codificados / decodificados por padrão.
Saji Xavier
@Tommy - mesmo se usar hmset / hgetall, pode ser necessário codificar / decodificar tipos que não são suportados pelo redis.
Saji Xavier
1
Discordar sobre "... a última operação é O (N)." N sendo o número de campos que você tem link para a chave. Fazer N SET / GET ou 1 HGET / HSET tem a mesma complexidade. Veja: redis.io/commands/hmset Time-wise, HGET / HSET são transações atômicas e, portanto, são executadas mais rapidamente por REDIS. Você está apenas movendo a complexidade do Redis para o código Python.
ZettaCircl
A vantagem do hmset é a possibilidade de recuperar apenas algumas subpartes do dicionário. Com json perdemos isso, então é tão bom quanto pickle ou outra coisa.
Jorge Leitão
5

Pode-se considerar o uso de MessagePack, que é endossado pelo 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 e aioredis

Ohad Lahav
fonte
4

Outra maneira de abordar o assunto:

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!

Não testei a eficiência / velocidade.

roberto
fonte
4

HMSET está obsoleto. Agora você pode usar HSET com um dicionário da seguinte maneira:

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
fonte
Obrigado! Estou tentando encontrar o médico onde tudo isso está explicado. Você sabe onde é. Por exemplo, para que servem os dois "Nones".
NealWalters,
@NealWalters: Consulte a linha na página de comando HMSET - redis.io/commands/hmset para aviso de descontinuação.
Saransh Singh
3

O comando redis SET armazena uma string, não dados arbitrários. Você pode tentar usar o comando redis HSET para armazenar o dicionário como um hash redis com algo como

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

mas os tipos de dados redis e python não estão bem alinhados. Os dictes Python podem ser aninhados arbitrariamente, mas um hash redis vai exigir que seu valor seja uma string. Outra abordagem que você pode adotar é converter seus dados Python em string e armazená-los no redis, algo como

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

e então, quando você retirar a string, precisará analisá-la para recriar o objeto Python.

Jesusaur
fonte
1
ele pode converter seus dados para json e armazenar o resultado no redis
Narcisse Doudieu Siewe
0

Experimente rejson-py, que é relativamente novo desde 2017. Veja esta introdução .

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

Se você não sabe exatamente como organizar os dados no Redis, fiz alguns testes de desempenho, incluindo a análise dos resultados. O dicionário que usei ( d ) tinha 437.084 chaves (formato md5), e os valores desta forma:

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

Primeiro teste (inserir dados em um mapeamento de valor-chave 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

Segundo teste (inserir dados diretamente nas chaves 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 você pode ver, no segundo teste, apenas os valores 'info' devem ser analisados, porque hgetall (chave) já retorna um dict, mas não um aninhado.

E, claro, o melhor exemplo de usar Redis como dictos de python, é o Primeiro Teste

Tavy
fonte