【问题标题】:Redis 10x more memory usage than dataRedis 内存使用量比数据多 10 倍
【发布时间】:2012-04-17 18:34:49
【问题描述】:

我正在尝试在 redis 中存储一个单词表。表演很棒。

我的方法是创建一个名为“words”的集合并通过“sadd”添加每个新单词。

当添加一个 15.9 MB 且包含大约一百万字的文件时,redis-server 进程会消耗 160 MB 的内存。为什么我使用了 10 倍的内存,有没有更好的方法来解决这个问题?

【问题讨论】:

    标签: performance memory redis


    【解决方案1】:

    您是否尝试过持久化数据库(例如BGSAVE)、关闭服务器并重新启动?由于碎片行为,当它恢复并从保存的 RDB 文件中填充其数据时,它可能会占用更少的内存。

    另外:您使用的是哪个版本的 Redis?看看this blog post - 它说从 2.4 版开始,碎片化已部分解决。

    【讨论】:

      【解决方案2】:

      对于我的实验,最好将数据存储在哈希表/字典中。经过大量基准测试后,我遇到的最好的情况是存储在不超过 500 个键的哈希表数据条目中。

      我尝试了标准字符串设置/获取,对于 100 万个键/值,大小为 79 MB。如果您有像 1 亿这样的大数字,这将使用大约 8 GB,这将是非常巨大的。

      我尝试使用哈希存储相同的数据,对于相同的百万键/值,大小越来越小 16 MB。

      如果有人需要基准测试代码,请尝试一下,给我发邮件

      【讨论】:

      • 您是如何进行这些测量的?谢谢
      【解决方案3】:

      这是任何高效的数据存储所期望的:单词必须在内存中以由指针链接的单元格的动态数据结构进行索引。结构元数据、指针和内存分配器内部碎片的大小是数据比相应平面文件占用更多内存的原因。

      Redis 集以哈希表的形式实现。这包括:

      • 以几何方式增长的指针数组(2 的幂)
      • 当增量重新散列处于活动状态时,可能需要第二个数组
      • 单链表单元表示哈希表中的条目(3 个指针,每个条目 24 个字节)
      • Redis 对象包装器(每个值一个)(每个条目 16 个字节)
      • 实际数据本身(每个都以 8 字节为前缀表示大小和容量)

      以上所有大小都是针对 64 位实现给出的。考虑到内存分配器开销,对于使用 jemalloc 分配器 (>= 2.4) 的最新版本的 Redis,Redis 每个集合项(在数据之上)至少占用 64 个字节

      Redis 为某些数据类型提供了memory optimizations,但它们不涵盖字符串集。如果你真的需要优化集合的内存消耗,你可以使用一些技巧。我不会只为 160 MB 的 RAM 执行此操作,但如果您有更大的数据,您可以这样做。

      如果您不需要集合的并集、交集、差集功能,那么您可以将单词存储在哈希对象中。好处是如果哈希对象足够小,Redis 可以使用 zipmap 自动优化哈希对象。 zipmap 机制在 Redis >= 2.6 中已被 ziplist 取代,但想法是一样的:使用可放入 CPU 缓存中的序列化数据结构,以获得性能和紧凑的内存占用。

      为了保证散列对象足够小,数据可以按照某种散列机制进行分布。假设你需要存储1M个项目,添加一个词可以通过以下方式实现:

      • 以 10000 为模散列(在客户端完成)
      • HMSET 单词:[hashnum] [word] 1

      而不是存储:

      words => set{ hi, hello, greetings, howdy, bonjour, salut, ... }
      

      你可以存储:

      words:H1 => map{ hi:1, greetings:1, bonjour:1, ... }
      words:H2 => map{ hello:1, howdy:1, salut:1, ... }
      ...
      

      要检索或检查单词的存在,它是相同的(散列并使用 HGET 或 HEXISTS)。

      使用此策略,如果哈希的模数为 根据 zipmap 配置(或 Redis >= 2.6 的 ziplist)选择:

      # Hashes are encoded in a special way (much more memory efficient) when they
      # have at max a given number of elements, and the biggest element does not
      # exceed a given threshold. You can configure this limits with the following
      # configuration directives.
      hash-max-zipmap-entries 512
      hash-max-zipmap-value 64
      

      注意:这些参数的名称已随着 Redis >= 2.6 更改。

      这里,1M 项的模 10000 意味着每个哈希对象有 100 个项,这将保证所有这些项都存储为 zipmaps/ziplists。

      【讨论】:

      • 引人入胜的详细答案;我不知道。谢谢@Didier!
      • 好的,非常感谢,我非常肯定这将解决我的问题。是的,160mb 还不错,但我希望可以处理高达 1gb 的纯字数据,并且不希望它飙升到 10gb。再次感谢,感谢详细的回答。
      • @Didier - 很好的答案!虽然有几个更正 a) 哈希表条目是一个单链表,而不是双链表,但 24 字节开销是正确的 b) Redis 对象包装器不适用于每个集合/哈希条目。它仅适用于顶级键/值对 - 因此开销是恒定的 c) 您可能希望指出 zipmap 在 2.6/unstable 中已弃用,并且 ziplist 执行相同的操作。
      • @SripathiKrishnan - 谢谢,我已经更新了我的答案。我仍然认为 robj 的使用适用于所有设置的键。我参考了 redis.c 中的 setDictType 结构和相应的函数,它们定义了这种行为。
      • @DidierSpezia - 重新。 robj 用法:是的,你是对的。不知道我是怎么忽略那个包装的!
      猜你喜欢
      • 2016-08-17
      • 2013-02-05
      • 2013-02-01
      • 2011-06-11
      • 2015-03-14
      • 1970-01-01
      • 1970-01-01
      • 2022-01-26
      • 2012-08-18
      相关资源
      最近更新 更多