当前位置 : 首页 » 互动问答 » 正文

Redis字符串与Redis哈希表示JSON:效率?

分类 : 互动问答 | 发布时间 : 2013-05-04 22:08:18 | 评论 : 3 | 浏览 : 95886 | 喜欢 : 218

我想将JSON有效负载存储到redis中。我有两种方法可以做到这一点:

  1. 一个使用简单的字符串键和值。
    key:user,value:payload(整个JSON blob,可以是100-200 KB)

    SET user:1 payload

  2. 使用hashes

    HSET用户:1用户名“someone”
    HSET用户:1个位置“NY”
    HSET用户:1生物“超过100行的字符串”

请记住,如果我使用散列,则值长度是不可预测的。它们并非都是短的,例如上面的生物例子。

哪种内存效率更高?使用字符串键和值,还是使用哈希?

回答(3)

  • 1楼
  • 这篇文章可以提供很多见解: http://redis.io/topics/memory-optimization

    有很多方法可以在Redis中存储一个对象数组(spoiler:我喜欢选项1对于大多数用例):

    1. 将整个对象作为JSON编码的字符串存储在单个键中,并使用集合(或列表,如果更合适)跟踪所有对象。例如:

       INCR id:users
      SET用户:{id}'{“name”:“Fred”,“age”:25}'
      SADD用户{id}
      

      一般来说,这可能是大多数情况下最好的方法。如果Object中有很多字段,您的对象不会与其他对象嵌套,并且您一次只能访问一小部分字段,最好选择选项2.

      Advantages:考虑一个“好习惯”。每个Object都是一个完整的Redis密钥。 JSON解析很快,特别是当您需要同时访问此Object的许多字段时。 缺点:当您只需要访问单个字段时速度较慢。

    2. 将每个Object的属性存储在Redis哈希中。

       INCR id:users
      HMSET用户:{id}名称“Fred”年龄25岁
      SADD用户{id}
      

      Advantages:被认为是“良好做法”。每个Object都是一个完整的Redis密钥。无需解析JSON字符串。 缺点:当您需要访问Object中的所有/大多数字段时,可能会更慢。此外,嵌套对象(对象内的对象)无法轻松存储。

    3. 将每个Object作为JSON字符串存储在Redis哈希中。

       INCR id:users
      HMSET用户{id}'{“name”:“Fred”,“age”:25}'
      

      这允许你巩固一点,只使用两个键而不是许多键。明显的缺点是你不能在每个用户对象上设置TTL(和其他东西),因为它只是Redis哈希中的一个字段,而不是一个完整的Redis密钥。

      Advantages:JSON解析很快,特别是当您需要同时访问此Object的许多字段时。减少主要名称空间的“污染”。 缺点:当您拥有大量对象时,与#1相同的内存使用量。当您只需要访问单个字段时,比#2慢。可能不被视为“良好做法”。

    4. 将每个Object的每个属性存储在专用密钥中。

       INCR id:users
      SET用户:{id}:名称“Fred”
      SET用户:{id}:25岁
      SADD用户{id}
      

      根据上面的文章,这个选项是几乎从不首选(除非Object的属性需要具有特定的TTL或者其他东西)。

      Advantages:对象属性是完整的Redis密钥,这可能不适合您的应用程序。 缺点:慢,使用更多内存,而不是“最佳实践”。很多污染主键名称空间。

    总结概要

    选项4通常不是首选。选项1和2非常相似,它们都很常见。我更喜欢选项1(一般来说),因为它允许您存储更复杂的对象(具有多层嵌套等)。当您非常关心关于不污染主键名称空间(即您不想要)时,使用选项3数据库中有很多键,你不关心TTL,键分片等等。

    如果我在这里弄错了,请考虑留下评论并允许我在贬低之前修改了答案。谢谢! :)

  • 2楼
  • 这取决于您访问数据的方式:

    转到选项1:

    • 如果您在大多数访问中使用大多数字段。
    • 如果可能的键存在差异

    转到选项2 :

    • 如果您在大多数访问中仅使用单个字段。
    • 如果您始终知道哪些字段可用

    PS:根据经验,请选择在大多数情况下需要较少查询的选项你的用例。

  • 3楼
  • 对一组给定答案的一些补充:

    首先,如果你要有效地使用Redis散列,你必须知道 密钥计数最大数量和值最大值 - 否则,如果它们打破hash-max-ziplist-value或hash-max-ziplist-entries,Redis会将其转换为引擎盖下几乎常用的键/值对。 (请参阅hash-max-ziplist-value,hash-max-ziplist-entries)并且从哈希选项中打破引擎真的很糟糕,因为Redis中的每个常用键/值对每对使用+90字节。

    这意味着如果你从选项2开始并意外地突破max-hash-ziplist-value,你将获得每个EACH ATTRIBUTE +90字节的内部用户模型! (实际上不是+90但是+70见下面的控制台输出)

    #你需要me-redis和awesome-print gems来运行确切的代码
     redis = Redis.include(MeRedis).configure(hash_max_ziplist_value:64,hash_max_ziplist_entries:512).new
      =>#<Redis客户端v4.0.1 for redis://127.0.0.1:6379/0>
     > redis.flushdb
      >“好的”
     > ap redis.info(:记忆)
        {
                    “used_memory”=>“529512”,
              **“used_memory_human”=>“517.10K”**,
                ....
        }
      =>无
     #me_set('t:i'...)与hset相同('t:i / 512',i%512 ...)
     #txt是一些约56K长的英文字典书,
     #所以我们只从它中取一些随机的63个符号的字符串
     > redis.pipelined {10000.times {| i | redis.me_set(“t:#{i}”,txt [rand(50000),63])}}; :完成
     =>:完成
     > ap redis.info(:记忆)
      {
                   “used_memory”=>“1251944”,
             **“used_memory_human”=>“1.19M”**,每个键值/#72b
                .....
      }
      > redis.flushdb
      >“好的”
      #setting **只有一个值**每个哈希值为512个值的+1个字节等于将它们全部设置为+1个字节
      > redis.pipelined {10000.times {| i | redis.me_set(“t:#{i}”,txt [rand(50000),i%512 == 0?65:63])}}; :完成
      > ap redis.info(:记忆)
       {
                   “used_memory”=>“1876064”,
             “used_memory_human”=>“1.79M”,每对#~134字节
              ....
       }
        redis.pipelined {10000.times {| i | redis.set(“t:#{i}”,txt [rand(50000),65])}};
        ap redis.info(:memory)
        {
                 “used_memory”=>“2262312”,
              “used_memory_human”=>“2.16M”,每对#~155字节,即+90字节
               ....
        }
    

    对于TheHippo的回答,对选项1的评论具有误导性:

    hgetall / hmset / hmget如果你需要所有字段或多个get / set操作,那么就是救援。

    对于BMiner的答案。

    第三个选项是实际上非常有趣,对于具有max(id)<has-max-ziplist-value的数据集,此解决方案具有O(N)复杂度,因为,令人惊讶的是,Reddis将小哈希存储为长度/键/值对象的类似数组的容器!

    但很多次哈希只包含几个字段。当散列较小时,我们可以只将它们编码为O(N)数据结构,就像具有长度前缀键值对的线性数组一样。由于我们只在N很小时才这样做,因此HGET和HSET命令的分摊时间仍然是O(1):一旦它包含的元素数量增长太多,哈希将被转换为真正的哈希表<notranslate >但你不应该担心,你会非常快地打破hash-max-ziplist-entries,而你现在实际上是在解决方案编号1.

    第二个选项很可能会转到第四个解决方案,因为问题陈述:

    请记住,如果我使用哈希值,则值长度是不可预测的。它们并不简单,例如上面的生物示例。

    正如您已经说过的那样:第四种解决方案是每个属性最昂贵的+70字节肯定。

    我的建议如何优化这样的数据集:< notranslate>你有两个选择:

    如果你不能保证某些用户属性的最大大小而不是第一个解决方案,并且内存问题至关重要 在redis中存储之前压缩用户json。

    如果可以强制所有属性的最大大小。 您可以设置hash-max-ziplist-entries / value,并使用哈希作为每个用户表示的一个哈希值或者作为哈里斯指南的主题中的哈希内存优化:

    1. 并将用户存储为json字符串。无论哪种方式,您还可以压缩长用户属性。

    2. https://redis.io/topics/memory-optimizationhttps://redis.io/topics/memory-optimization and store user as json string. Either way you may also compress long user attributes.

相关阅读:

How do I test for an empty JavaScript object?

Can comments be used in JSON?

Convert JS object to JSON string

Safely turning a JSON string into an object

How to parse JSON in Java

Select objects based on value of variable in object using jq

Json.net serialize/deserialize derived types?

Parsing values from a JSON file?

Parse JSON in JavaScript?

How to POST JSON data with Curl from Terminal/Commandline to Test Spring REST?