2013-03-05 75 views
37

hmset函數可以設置每個字段的值,但我發現如果值本身是一個複雜的結構化對象,則從hget返回的值是一個序列化字符串,而不是原始對象如何在redis中存儲複雜對象(使用redis-py)

例如

images= [{'type':'big', 'url':'....'}, 
    {'type':'big', 'url':'....'}, 
    {'type':'big', 'url':'....'}] 

redis = Redis() 
redis.hset('photo:1', 'images', images) 

i = redis.hget('photo:1', 'images') 
print type(i) 

i的類型是一個字符串,而不是一個對象蟒,是否有任何方式來手動除了解決這個問題解析每個字段?

回答

40

您不能在Redis中創建嵌套結構,這意味着您無法(例如)在原生redis哈希映射中存儲本機重做列表。

如果你確實需要嵌套結構,你可能只想存儲一個JSON-blob(或類似的東西)。另一種選擇是將「id」/ key存儲到不同的redis對象中作爲map key的值,但這需要多次調用服務器才能獲得完整的對象。

+0

哦,還有一件事;使用EVAL(服務器端ruby腳本)可能會產生一個奇怪的複合查詢:http://redis.io/commands/eval – 2013-03-05 09:54:05

-2

你可以只存儲您的結構是,做一個「EVAL」從字符串轉換爲對象:

images= [{'type':'big', 'url':'....'}, 
{'type':'big', 'url':'....'}, 
{'type':'big', 'url':'....'}] 
redis = Redis() 
redis.hset('photo:1', 'images', images) 

i = eval(redis.hget('photo:1', 'images')) 
print type(i) #type of i should be list instead of string now 
+11

這是不必要的危險:任意Python代碼通過'eval()'來評估。 JSON方法不會因此而受到影響。 – EOL 2014-03-15 15:53:51

58

其實,你可以使用Python對象存儲在Redis的內置模塊pickle

這裏是一個例子。

import pickle 
import redis 

r = redis.StrictRedis(host='localhost', port=6379, db=0) 
obj = ExampleObject() 
pickled_object = pickle.dumps(obj) 
r.set('some_key', pickled_object) 
unpacked_object = pickle.loads(r.get('some_key')) 
obj == unpacked_object 
+17

這很危險:unpickling可以執行代碼。 JSON解決方案更強大。 – EOL 2014-03-15 15:55:11

+0

@EOL你可以在這方面做進一步的工作嗎?爲什麼會執行代碼是一件壞事? – ealeon 2014-07-10 17:14:51

+1

我的意思是酸洗允許執行* untrusted *代碼,因爲有問題的代碼可能是惡意的(它可以擦除文件等),所以最好避免這種代碼。 – EOL 2014-07-11 12:25:09

4

我創建了一個庫,SubRedis,它可以讓你創建Redis的更復雜的結構/層次。如果你給它一個redis實例和一個前綴,它會給你一個幾乎完全有能力和獨立的redis實例。

redis = Redis() 
photoRedis = SubRedis("photo:%s" % photoId, redis) 
photoRedis.hmset('image0', images[0]) 
photoRedis.hmset('image1', images[1]) 
... 

SubRedis只是將傳入的字符串作爲前綴添加到平面redis數據結構中。我發現這對於一個模式來說是一個方便的包裝,我最終在redis中做了很多事情 - 預先添加一些id來嵌套一些數據。

29

JSON實例:

import json 
import redis 

r = redis.StrictRedis(host='localhost', port=6379, db=0) 

images= [ 
    {'type':'big', 'url':'....'}, 
    {'type':'big', 'url':'....'}, 
    {'type':'big', 'url':'....'}, 
] 

json_images = json.dumps(images) 
r.set('images', json_images) 
unpacked_images = json.loads(r.get('images')) 
images == unpacked_images 

蟒蛇3:

unpacked_images = json.loads(r.get('images').decode('utf-8')) 
images == unpacked_images 
+0

從'redis.get()'獲取btyes對象是否正常? python 3 – 2016-01-20 16:08:27

+3

@shangyeshen看起來像'json.loads()'和python3中的'bytes'對象不太合適。您需要將'redis.get()'的'bytes'結果解碼爲python3'str'。看到新的編輯。 – CivFan 2016-01-20 17:10:43

2

可以使用RedisWorks庫。

pip install redisworks

>>> from redisworks import Root 
>>> root = Root() 
>>> root.something = {1:"a", "b": {2: 2}} # saves it as Hash 
>>> print(root.something) # loads it from Redis 
{'b': {2: 2}, 1: 'a'} 
>>> root.something['b'][2] 
2 

它轉換蟒類型Redis的類型,反之亦然。

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

聲明:我寫了圖書館。下面是代碼:https://github.com/seperman/redisworks

1

這裏是圍繞Redis的這醬菜/ unpickles數據結構一個簡單的包裝:

from redis import Redis 
from collections import MutableMapping 
from pickle import loads, dumps 


class RedisStore(MutableMapping): 

    def __init__(self, engine): 
     self._store = Redis.from_url(engine) 

    def __getitem__(self, key): 
     return loads(self._store[dumps(key)]) 

    def __setitem__(self, key, value): 
     self._store[dumps(key)] = dumps(value) 

    def __delitem__(self, key): 
     del self._store[dumps(key)] 

    def __iter__(self): 
     return iter(self.keys()) 

    def __len__(self): 
     return len(self._store.keys()) 

    def keys(self): 
     return [loads(x) for x in self._store.keys()] 

    def clear(self): 
     self._store.flushdb() 


d = RedisStore('redis://localhost:6379/0') 
d['a'] = {'b': 1, 'c': 10} 
print repr(d.items()) 
# this will not work: (it updates a temporary copy and not the real data) 
d['a']['b'] = 2 
print repr(d.items()) 
# this is how to update sub-structures: 
t = d['a'] 
t['b'] = 2 
d['a'] = t 
print repr(d.items()) 
del d['a'] 

# Here is another way to implement dict-of-dict eg d['a']['b'] 
d[('a', 'b')] = 1 
d[('a', 'b')] = 2 
print repr(d.items()) 
# Hopefully you do not need the equivalent of d['a'] 
print repr([{x[0][1]: x[1]} for x in d.items() if x[0][0] == 'a']) 
del d[('a', 'b')] 
del d[('a', 'c')] 

如果您喜歡在Redis的明文可讀的數據(泡菜店它的二進制版本),你可以用repr和pickle.loads替換pickle.dumps和ast.literal_eval。對於json,請使用json.dumps和json.loads。

如果您始終使用簡單字符串的鍵,則可以從鍵中移除酸洗。

相關問題