【问题标题】:How to store and retrieve a dictionary with redis如何使用 redis 存储和检索字典
【发布时间】:2015-11-23 10:28:42
【问题描述】:
# I have the dictionary my_dict
my_dict = {
    'var1' : 5
    'var2' : 9
}
r = redis.StrictRedis()

我将如何存储 my_dict 并使用 redis 检索它。例如下面的代码不起作用。

#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

【问题讨论】:

    标签: python redis


    【解决方案1】:

    您可以通过hmset 进行操作(可以使用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'}
    

    【讨论】:

    • 如果它是嵌套数据结构而不是简单的 dict,例如包含一些数组等。序列化你的数据 json.dumps() 写为字符串并从 redis 用户 json.loads() 检索后将其反序列化回 python数据结构
    • json.dumps()json.loads() 只有在你的字典键始终是字符串的情况下才有效。如果你不是,那么你可以考虑使用泡菜。
    • json 与 bytes 不兼容,因此 json 序列化不是一个全局解决方案,例如,如果您有一个带有 bytes 值的 dict,这将不起作用。
    • 请注意,hmset 的文档没有告诉您这一点,但如果您尝试存储空字典,则会引发 DataError。
    • @Pradeep 我们如何使密钥动态化。假设每 15 分钟插入一次数据,那么我如何使密钥动态化
    【解决方案2】:

    你可以腌制你的字典并保存为字符串。

    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)
    

    【讨论】:

    • 这是真的,但是根据读写的速率,这可能会增加严重的开销。酸洗是一个缓慢的操作
    • 请注意,如果这与用户输入一起使用,则您的服务器 is prone to remote code exectionpickle.loads 只能用于 100% 受信任的数据
    • 泡菜如果处理不当会很危险。在将数据存储到 Redis 之前,使用 msgpack 更好地序列化数据。
    • Pickling 还有一个重要的问题是你无法调试 Redis 中存储的数据,因为它们是二进制的。
    【解决方案3】:

    由于其他人已经给出了基本答案,我想补充一些。

    以下是REDIS 中的命令,用于使用HashMap/Dictionary/Mapping 类型值执行基本操作。

    1. HGET => 返回传递的单个键的值
    2. HSET => 设置/更新单个键的值
    3. HMGET => 返回传递的单个/多个键的值
    4. HMSET => 设置/更新多个键的值
    5. HGETALL => 返回映射中的所有(键、值)对。

    以下是它们各自在redis-py库中的方法:-

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

    如果映射不存在,上述所有 setter 方法都会创建映射。 如果映射/映射中的键不存在,上述所有 getter 方法都不会引发错误/异常。

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

    我希望,它能让事情更清楚。

    【讨论】:

    • 如何使密钥动态化
    【解决方案4】:

    另一种方式:你可以使用RedisWorks库。

    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
    

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

    【讨论】:

    • 请注意,如果您将变量设置为字典,RedisWorks 在后台使用hmset,因此如果您执行root.something = {},您将收到DataError,因为hmset 没有t 允许空字典。我提到这一点是因为 redis 的文档没有告诉你这一点。
    • 有趣。是的,它确实使用了hmset。我会调查一下。 @hlongmore
    • 但是,它还能支持字典中的字节吗?
    • 顺便说一句,Redis.hmset() 已被弃用。你应该使用 Redis.hset() 代替。
    • 是的,当我有机会时,我需要修复它......也非常欢迎公关!
    【解决方案5】:

    如果你想在redis中存储一个python dict,最好将它存储为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)
    

    在检索反序列化时使用 json.loads

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

    那些没有被 json 函数序列化的类型(例如字节)呢?

    您可以为无法由 json 函数序列化的类型编写编码器/解码器函数。例如。为字节数组编写base64/ascii编码器/解码器函数。

    【讨论】:

    • 我对此投了反对票,因为某些字典无法序列化为 JSON,例如,具有字节值的字典。
    • 对于默认无法编解码的类型,可以编写编解码函数(根据需求,例如base64/ascii编码)。
    • @Tommy - 即使使用 hmset/hgetall ,您也可能需要对 redis 不支持的类型进行编码/解码。
    • 不同意“...后面的操作是 O(N)。” N 是您链接到密钥的字段数。执行 N SET/GET 或 1 HGET/HSET 具有相同的复杂性。请参阅:redis.io/commands/hmset 时间方面,HGET/HSET 是原子事务,因此 REDIS 执行得更快。您只是将复杂性从 Redis 转移到 Python 代码。
    • 导入 json 丢失。
    【解决方案6】:

    根据Redis docs,不推荐使用 HMSET。您现在可以将HSET 与字典一起使用,如下所示:

    import redis
    
    r = redis.Redis('localhost')
        
    key = "hashexample" 
    entry = { 
        "version":"1.2.3", 
        "tag":"main", 
        "status":"CREATED",  
        "timeout":"30"
    }
    r.hset(key, mapping=entry)
    

    【讨论】:

    • 谢谢!我正在尝试找到所有这些都被详细说明的文档。你知道在哪里吗。例如,这两个“无”是干什么用的。
    • @NealWalters:请参阅 HMSET 命令页面上的行 - redis.io/commands/hmset 以获取弃用警告。
    【解决方案7】:

    可以考虑使用redis认可的MessagePack

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

    使用msgpack-pythonaioredis

    【讨论】:

      【解决方案8】:

      redis SET 命令存储一个字符串,而不是任意数据。您可以尝试使用 redis HSET 命令将 dict 存储为 redis 哈希,例如

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

      但是 redis 数据类型和 python 数据类型并没有完全对齐。 Python dicts 可以任意嵌套,但是 redis 哈希将要求您的值是一个字符串。您可以采取的另一种方法是将您的 python 数据转换为字符串并将其存储在 redis 中,例如

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

      然后当你得到字符串时,你需要解析它以重新创建 python 对象。

      【讨论】:

      • 他可以将他的数据转换成json并存储到redis中
      【解决方案9】:

      解决问题的另一种方法:

      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!
      

      我没有测试它的效率/速度。

      【讨论】:

        【解决方案10】:

        如果你不知道如何在 Redis 中组织数据,我做了一些性能测试,包括结果解析。 我使用的字典 (d) 有 437.084 个键(md5 格式),这种形式的值:

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

        第一次测试(将数据插入到 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
        

        第二次测试(将数据直接插入 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
        

        如您所见,在第二个测试中,只需要解析 'info' 值,因为 hgetall(key) 已经返回一个 dict,但不是嵌套的。

        当然,将 Redis 用作 python 的 dicts 的最佳示例是 First Test

        【讨论】:

          【解决方案11】:

          DeprecationWarning:不推荐使用 Redis.hmset()。请改用 Redis.hset()。

          由于不推荐使用 HMSET,您可以使用 HSET:

          import redis
          
          r = redis.Redis(host='localhost', port=6379, decode_responses=True)
          r.hset('user:23', mapping={'id': 23, 'name': 'ip'})
          r.hgetall('user:23')
          

          【讨论】:

            【解决方案12】:

            试试rejson-py,这是自 2017 年以来相对较新的。看看这个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)
            

            【讨论】:

              【解决方案13】:

              在 Nameko(经常使用 redis 后端的 Python 微服务框架)的上下文中,您可以按如下方式使用 hmset:

              import uuid
              from nameko.rpc import rpc
              from nameko_redis import Redis
              
              class AirportsService:
                  name = "trips_service"
                  redis = Redis('development')
                  @rpc
                  def get(self, trip_id):
                      trip = self.redis.get(trip_id)
                      return trip
                  @rpc
                  def create(self, airport_from_id, airport_to_id):
                      trip_id = uuid.uuid4().hex
                      pyDict = {"from":airport_from_id, "to":airport_to_id}
                      self.redis.hmset(trip_id, pyDict)
                      return trip_id
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2013-11-28
                • 1970-01-01
                • 1970-01-01
                • 2016-10-21
                • 1970-01-01
                • 1970-01-01
                • 2020-03-25
                • 1970-01-01
                相关资源
                最近更新 更多