【问题标题】:Understanding hash Implementation and it's memory in Redis了解哈希实现及其在 Redis 中的内存
【发布时间】:2016-10-03 09:08:40
【问题描述】:

从文档中我们知道 redis 会压缩一个范围内的数据(默认为 512)。如果哈希范围超过 512,那么内存差异将是 10 倍。

我对 1 到 512 的哈希值做了一个小实验,发现了一些有趣的模式。

此图表示 1000 个散列占用的内存(以 KB 为单位),每个散列包含从 1 到 512 的条目。

正如您在此图中看到的那样。在某些时间间隔内存在陡峭。我理解 redis 中的哈希实现也遵循一些逻辑,当它达到一定范围时扩展大小,而不是为每个新条目增加它。从数字来看,它并没有始终遵循翻倍的模式,但是从 215 到 216 它确实翻了一番,从 4 MB 到 8 MB。从 420 到 421,它几乎增加了一半 8 MB 到 12 MB。在 215 内的陡坡中,我看不到它在 1/4、1/5 和 1/6 之间变化的任何模式。

根据我的观察,以下是我的问题:

  1. 谁能解释一下 hashmap 在内存和调整大小方面的内部情况?调整大小时遵循的逻辑是什么?
  2. 如果我不得不释放双倍内存来存储一个 215 到 216 的条目,为什么我不能限制我的应用程序的哈希值始终小于 215,除非系统最多需要它.
  3. 假设如果我想存储 100 万个散列,每个散列包含 250 个值,我需要 800MB。如果我将它们分成 125 个值的 2 个哈希值,即 125 个值的 200 万个哈希值,我需要 500MB。通过这种方式,我节省了 300 MB,这是巨大的!!这个计算对吗?在这种情况下我错过了什么吗?

提前致谢

【问题讨论】:

  • 我知道这与这个问题无关,但是您是否尝试过设置技术来减少数据占用?
  • @Karthikeyan Gopall,我认为最好再提供两个细节: 1. 你是如何使用内存的?使用 redis-cli 获取 'info Memory' 并获取字段 ''used_memory" 或 "used_memory_rss" 或其他地方的值 2. 你可以通过 "./redis-cli info |grep mem_allocator" 获得的内存分配器跨度>
  • @sel-fish 1) 我会从“used_memory”中得到它。在开始这个过程之前我会使用used_memory,在这个过程之后我会做同样的事情,它们的区别就是我在图中提到的。 2) mem_allocator:jemalloc-3.6.0

标签: memory hash redis hashmap


【解决方案1】:

你可以在文章Redis under the hood: Hash(part1) 和Redis under the hood: Hash(part2) 中找到关于哈希内部的完整描述。简而言之,内存每次都会变大:

  1. 您的哈希从 ziplist 移动到 dict 内部编码。
  2. 哈希填充因子强制 Redis 将哈希大小加倍。

请记住 - Redis 使用 dict 来处理密钥空间。因此,每次创建新密钥(任何类型)时,都将其放入密钥的内部哈希表中。所以这是相同的逻辑 - 当您向 Redis 添加新密钥时,它会增长为 dict。

【讨论】:

  • 正如@Karthikeyan Gopall 所说,在他的实验中,有1000 个哈希,这意味着redis 中总是只有1000 个key,因此它与redis rehash 无关。此外,每个哈希的条目数小于 512,因此每个哈希都使用 ziplist。
  • 是的,但是每次创建哈希键(任何类型)时,都会将其推送到键的内部哈希表中。所以这是相同的逻辑 - 处理 key spase 增长的 redis 内部哈希。
【解决方案2】:

由于你在redis中不断有1000个key,每个hash key只有字段号变化,而你的字段号低于512,所以这个现象只是jemalloc造成的。

这是我使用 libc 作为 mem_allocator 时的行为:

您可以通过以下方式重新制作您的 redis:

make MALLOC=libc

再次运行你的测试,看看你会得到什么。

回答您的问题:

  1. 有人可以向我解释一下 hashmap 在内存和调整大小方面的内部情况吗?调整大小时遵循的逻辑是什么?

    如前所述,您遇到的与redis本身无关。 Jemalloc 这样做是为了提高效率

  2. 如果我不得不释放双倍内存来存储一个 215 到 216 的条目,为什么我不能限制我的应用程序的哈希值始终小于 215,除非系统最多需要它.

    当然可以,只要能限制字段编号即可

  3. 假设如果我想存储 100 万个散列,每个散列包含 250 个值,我需要 800MB。如果我将它们分成 125 个值的 2 个哈希值,即 125 个值的 200 万个哈希值,我需要 500MB。通过这种方式,我节省了 300 MB,这是巨大的!!这个计算对吗?在这种情况下我错过了什么吗?

    我认为这不是正确的做法。也许你可以通过这样做来节省一些内存。但是,缺点是:当您将 100 万个哈希拆分为 200 万个时,redis 会进行重新哈希(这会占用您一些空间),并且您会花费更多时间来找到一个密钥,因为这会导致哈希冲突的机会更大.

【讨论】:

  • 是的,你是对的。我已经使用 libc 完成了相同的过程并理解了其中的区别。感谢分享知识:)
  • 很高兴答案有帮助。谢谢你给我带来这个问题:)
【解决方案3】:

@sel-fish 做对了。这都是关于内存分配器的。为了其他人的利益,我想添加更多信息。

我又做了一项实验,比较了在 jemalloc 和 libc 中执行相同操作所需的时间。为了更清楚,我在完全相同的情况下做了这两个实验。我在性能上找不到任何重大差异,libc 大多数时候都赢了。

我已附上截图。

正如您在图中看到的,jemalloc 的大小调整有点翻倍,而 libc 正在连续增加。

使用 libc 并没有显着的性能下降(花费时间)。实际上,与大多数领域相比,libc 花费的时间更少。

我也研究了几篇关于 libc 和 jemalloc 的文章,在我看来,对于这种情况 libc 胜出。

我也想听听其他人对他们的看法。

【讨论】:

  • 恐怕我要问一个与你的答案无关的问题,你能告诉我你是用什么工具画的吗?或者你能帮我在这里回答这个问题吗:superuser.com/questions/1090292/…
  • @sel-fish 这是一个名为 Zoho Reports (reports.zoho.com) 的在线 BI 工具。我将在实验过程中花时间,将它们写成 CSV,然后将它们推送到工具中并创建图表。
猜你喜欢
  • 2020-05-04
  • 1970-01-01
  • 2022-08-20
  • 2012-10-01
  • 2012-03-15
  • 1970-01-01
  • 2015-05-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多