2012-04-04 61 views
17

我有一个小问题。Redis的内存使用量比数据多10倍

我试图在redis中存储一个单词表。表现很棒。

我的做法是制作一个名为“单词”的集合,并通过“sadd”添加每个新单词。

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

由于提前

回答

75

那么这是所有有效的数据存储的预期:字必须在指针链接的单元格的动态数据结构的内存中索引。结构元数据,指针和内存分配器内部碎片的大小是数据比相应的平面文件占用更多内存的原因。

Redis集合实现为散列表。这包括:

  • 指针数组几何级数增长(二的幂)
  • 当增量重散列是活性
  • 表示在哈希表中的条目的单链接列表中的小区(第二阵列,可能需要3个指针,每个条目24个字节)
  • Redis的对象包装(每个值的一个)(每个条目16个字节)
  • 实际数据本身(它们中的每对由尺寸和容量)
个字节前缀

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

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

如果您不需要集合的联合,交集和差异功能,那么您可以将您的单词存储在散列对象中。好处是散列对象可以由Redis使用zipmap自动优化,只要它们足够小。在Redis> = 2.6中,zipmap机制已被ziplist取代,但想法相同:使用可装入CPU高速缓存的序列化数据结构,以获得高性能和紧凑的内存占用空间。

为了保证哈希对象足够小,可以根据一些哈希机制来分配数据。假设你需要存储100万件,增加一个字可以通过以下方式实现:

  • 哈希它模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)。

利用这种策略,显著存储器节省可以做到提供的散列的(对于Redis的或ziplist> = 2.6)的模被 根据zipmap配置选自:

# 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存储。

+0

迷人而详细的答案;我不知道。谢谢@Didier! – 2012-04-04 12:25:32

+0

好吧,非常感谢,我非常肯定,这将解决我的问题。对于160MB的版本来说,它的性能还不错,但我希望能够处理最多1GB的纯文字数据,并且不希望它达到10GB。再次感谢,感谢详细的答复。 – cwoebker 2012-04-04 15:35:09

+2

@Didier - 很好的回答!虽然a)哈希表条目是一个单链表,不是双精度,24字节的开销是正确的,但b)Redis对象包装不适用于每个集合/哈希条目。它只适用于最高级别的键/值对 - 所以开销是不变的c)您可能想要指出zipmap在2.6/unstable中不推荐使用,并且该ziplist可以做同样的事情。 – 2012-04-05 06:58:06

2

你尝试持久数据库(BGSAVE为例),关闭服务器,并得到它备份?由于存在碎片行为,当它恢复并从保存的RDB文件填充其数据时,可能会占用较少的内存。

另请参见:您与Redis合作的是哪种版本?看看this blog post--它表示从2.4版本开始就已经部分解决了碎片问题。

4

至于我的实验,最好将数据存储在散列表/字典中。我在很多基准测试后达成的最佳案例是存储在不超过500个密钥的散列表数据条目内。

我试过标准字符串set/get,对于1百万个键/值,大小为79 MB。如果您拥有大约1亿人的大数字,并且使用大约8 GB的数据,这非常巨大。

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

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

相关问题