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

cwo*_*ker 20 memory performance redis

我有一个小问题.

我想在redis中存储一个单词表.表现很棒.

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

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

提前致谢

Did*_*zia 83

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

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

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

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

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

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

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

  • hash it modulo 10000(在客户端完成)
  • HMSET词:[hashnum] [字] 1

而不是存储:

words => set{ hi, hello, greetings, howdy, bonjour, salut, ... }
Run Code Online (Sandbox Code Playgroud)

你可以存储:

words:H1 => map{ hi:1, greetings:1, bonjour:1, ... }
words:H2 => map{ hello:1, howdy:1, salut:1, ... }
...
Run Code Online (Sandbox Code Playgroud)

要检索或检查单词的存在,它是相同的(哈希它并使用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
Run Code Online (Sandbox Code Playgroud)

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

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

  • @Didier - 很棒的答案!几个更正虽然a)Hashtable条目是单个链表,而不是double,24字节开销是正确的虽然b)Redis对象包装器不适用于每个set/hash条目.它仅适用于顶级键/值对 - 因此开销是常量c)您可能想要指出在2.6/unstable中不推荐使用zipmap,并且该ziplist执行相同的操作. (2认同)

Muh*_*man 5

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

我尝试了标准字符串set/get,对于100万个键/值,大小为79 MB.如果您有大约1亿的大数字,它将使用大约8 GB,这是非常巨大的.

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

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