如何在redis中存储复杂对象(使用redis-py)

yua*_*uan 53 python redis

hmset函数可以设置每个字段的值,但我发现如果值本身是一个复杂的结构化对象,则从hget返回的值是序列化字符串,而不是原始对象

例如

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)
Run Code Online (Sandbox Code Playgroud)

i的类型是一个字符串,而不是一个python对象,除了手动解析每个字段之外,还有什么方法可以解决这个问题吗?

Kyr*_*kov 81

实际上,您可以使用内置模块pickle在redis中存储python对象.

这是一个例子.

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
Run Code Online (Sandbox Code Playgroud)

  • 这很危险:unpickling可以执行代码.JSON解决方案更加强大. (27认同)
  • @EOL我不认为你的redis是不受信任的来源.你无法阻止人们在脚下射击 (14认同)
  • @EOL:在我看来,JSON是可能的首选,但是有很多对象无法序列化为JSON. (4认同)
  • @KyryloPerevozchikov:好的,公平的,因为原来的问题确实在本地连接.没有从我这里投票.本地Redis可能仍然是不受信任来源的本地副本,因此我将在上面留下我的评论,它不会受到伤害.:) (2认同)
  • 使用 cPickle 比使用 pickle 更好,因为 cPickle 具有更好的性能。 (2认同)
  • 我刚刚用一些物体进行了测试。Pickle 比 Json blob 慢。对于转储和装载。 (2认同)

Jon*_*org 48

您无法在Redis中创建嵌套结构,这意味着您不能(例如)将本机redis列表存储在本机redis哈希映射中.

如果你真的需要嵌套结构,你可能只想存储一个JSON-blob(或类似的东西).另一种选择是将"id"/键存储到不同的redis对象作为映射键的值,但是需要多次调用服务器才能获得完整的对象.


Civ*_*Fan 44

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
Run Code Online (Sandbox Code Playgroud)

python 3:

unpacked_images = json.loads(r.get('images').decode('utf-8'))
images == unpacked_images
Run Code Online (Sandbox Code Playgroud)

  • @shangyeshen看起来`json.loads()`与python3中的`bytes`对象不一致.你需要将`red`.get()`的`bytes`结果解码成python3`str`.查看新编辑. (4认同)
  • 您还可以将 `decode_responses=True` 传递给 StrictRedis (或者只是版本 > 3.0 的 Redis /sf/ask/1331523581/ Between-strictredis-and-redis )。 (4认同)

小智 7

您可以将RedisLabs 中的RedisJSON与python 客户端一起使用。它支持嵌套数据结构。对于这样的任务非常有用。

示例中的一些代码:

   # 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'))
   )
Run Code Online (Sandbox Code Playgroud)


Dou*_* T. 6

我创建了一个库SubRedis,它使您可以在Redis中创建更复杂的结构/层次结构。如果为它提供一个redis实例和一个前缀,它将为您提供几乎完全有能力且独立的redis实例。

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

SubRedis最终只是将传入的字符串作为前缀添加到平面redis数据结构上。我发现这对于我最终在redis中做很多工作的模式来说是一个方便的包装器-预先添加一些ID以嵌套一些数据。


Sep*_*man 6

您可以使用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
Run Code Online (Sandbox Code Playgroud)

它将 python 类型转换为 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'>
Run Code Online (Sandbox Code Playgroud)

免责声明:我写了这个库。这是代码: https: //github.com/seperman/redisworks


Cur*_*lop 5

这是一个围绕 Redis 的简单包装器,它腌制/取消腌制数据结构:

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')]
Run Code Online (Sandbox Code Playgroud)

如果您更喜欢 redis 中的纯文本可读数据(pickle 存储它的二进制版本),您可以将 pickle.dumps 替换为 repr,将 pickle.loads 替换为 ast.literal_eval。对于 json,使用 json.dumps 和 json.loads。

如果您总是使用简单字符串的键,则可以从键中删除酸洗。