Redis的内存使用量比数据多10倍

时间:2012-04-04 03:33:37

标签: performance memory redis

我有一个小问题。

我正在尝试将单词列表存储在redis中。表现很棒。

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

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

先谢谢

3 个答案:

答案 0 :(得分:81)

这对于任何有效的数据存储都是可取的:必须在由指针链接的单元格的动态数据结构中将字索引到内存中。结构元数据,指针和内存分配器内部碎片的大小是数据占用比相应平面文件更多内存的原因。

Redis集实现为哈希表。这包括:

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

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

Redis为某些数据类型提供memory optimizations,但它们不包括字符串集。如果你真的需要优化集合的内存消耗,你可以使用一些技巧。我不会只为160 MB的RAM做这个,但如果你有更大的数据,这就是你可以做的。

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

为了保证散列对象足够小,可以根据某种散列机制分发数据。假设您需要存储1M项,可以通过以下方式实现添加单词:

  • 将其模数为10000(在客户端完成)
  • HMSET字:[hashnum] [字] 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。

答案 1 :(得分:5)

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

我尝试了标准字符串set / get,对于100万个键/值,大小为79 MB。如果您拥有大约1亿的大数字,那将是非常巨大的。

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

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

答案 2 :(得分:2)

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

另外:您使用的是什么版本的Redis?看看this blog post - 它表示从版本2.4开始,碎片已部分解决。