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

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)

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

122464 次浏览

您不能在 Redis 创建嵌套结构,这意味着您不能(例如)在本地 Redis 散列图中存储本地 Redis 列表。

如果您真的需要嵌套结构,您可能只需要存储一个 JSON-blob (或类似的东西)。另一种选择是将“ id”/key 作为 map key 的值存储到另一个 redis 对象,但这需要对服务器进行多次调用才能获得完整的对象。

你只需要按原样存储你的结构,然后做一个“ 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

实际上,您可以使用内置模块 泡菜在 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

我创建了一个库 SubRedis,它允许您在重排中创建更复杂的结构/层次结构。如果您给它一个 redis 实例和一个前缀,它会给您一个几乎完全有能力和独立的 redis 实例。

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

SubRedis 最终只是将传递给它的字符串作为前缀添加到平面 Redis 数据结构上。我发现这是一个方便的模式包装器,我最终在重排中做了很多工作——预先设置一些 id 来嵌套一些数据。

如果您的数据是 JSON 可序列化,那么这可能是比将 Python pickles 保存到外部数据库更好的选择,因为它是 Python 之外更常见的标准,它本身更具人类可读性,并且避免使用 相当大的攻击矢量

例子:

import json
import redis


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


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


# Convert python dict to JSON str and save to Redis
json_images = json.dumps(images)
r.set('images', json_images)


# Read saved JSON str from Redis and unpack into python dict
unpacked_images = json.loads(r.get('images'))
images == unpacked_images

巨蟒3:

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

可以使用 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

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

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

下面是 Redis 的一个简单包装器,它可以对数据结构进行 pickle/unpickle:

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 中的明文可读数据(pickle 存储该数据的二进制版本) ,那么可以使用 repr 替换 pickle.dump,使用 ast.Literal _ eval 替换 pickle.load。对于 json,使用 json.dump 和 json.load。

如果您总是使用键,这是一个简单的字符串,您可以删除酸洗从关键。

可以将来自 RedisLabs 的 RedisJSONPython 的客户端一起使用。 它支持嵌套的数据结构,对于这样的任务非常有用。

例子中的一些代码:

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

我最近遇到过一个类似的用例: 在 redis hash 中存储一个复杂的数据结构。

我认为解决这个问题的最好方法是将 json 对象序列化为字符串并将其存储为另一个对象的值。

打字稿例子

对象存储在散列表中

const payload = {
"k1":"v1",
"k2": "v2",
"k3": {
"k4":"v4",
"k5":"v5"
}
}

将此有效载荷存储为

await redis.hmset('hashMapKey', {somePayloadKey: JSON.stringify(payload) });

这可以检索为

      const result = await redis.hgetall('hashMapKey');
const payload = JSON.parse(result.somePayloadKey);

HMSET 和 HGETALL 是重排的 HMSET 和 HGETALL 的 Teddy等价物。

希望这个能帮上忙。

 for saving object in redis first convert object into stringify JSON
StringifyImages = json.dumps(images)
redis.set('images', StringifyImages)


# Read stringify object from redis and parse it
ParseImages = json.loads(redis.get('images'))