【问题标题】:Why did my redis fill up so quickly?为什么我的 redis 这么快就满了?
【发布时间】:2016-02-16 10:59:03
【问题描述】:

我是using redis to store bits to track hourly, daily, weekly, and monthly active users。每次我有一个具有唯一 user、hour 或 eventType 的事件时,都会存储一位。

我正在使用的redis命令(节点客户端)是client.setbit(key, id, 1);

其中key 类似于login-mobile-2015-11-14-04 而id 是一个六位整数(指用户)。

在短短几天内,我达到了 heroku redis 免费计划的 25MB 限制,但最大实际数据量(基于唯一事件和用户的数量为每天192000,比25MB。我怀疑是我的密钥太长或问题出在哪里,但我对 redis 不太了解,所以想问问。

【问题讨论】:

  • 根据 redis 命令, SETBIT 创建一个字符串,其长度可以保存曾经使用过的最大偏移值(在您的情况下是用户的 id)并将该位设置为 0x00 或 0x01(取决于您的 SETBIT为 0 或 1)。在您的情况下,这意味着每天最多使用 (999999 / 8) 字节 + 26 字节(键名)+ redis 开销(???)。我不知道heroku是怎么计算的,我有一个空白的新redis和SETBIT key 0 1 > SETBIT key 999999 1 > SAVE 结果dump.rdb size = 1484 bytes。
  • Ken,RDB 转储总是比内存存储小得多。没有可比性。

标签: node.js heroku redis node-redis nosql


【解决方案1】:

答案在 Redis 字符串内部数据结构中。 Redus 使用SDS 字符串,有时了解它的工作原理非常重要。最重要的是SDS中的内存分配策略。

实际上,当它小于 SDS_MAX_PREALLOC(1MB) 时,它只是将原始大小翻倍。这类似于内存不足时的 C++ 向量分配策略。这就是为什么字符串追加操作不需要每次都分配内存的原因。

为什么这对你的问题很重要。 SETBIT 仅使用字符串作为容器,因此使用六位整数作为 userId(在最坏的情况下)999,999 / 8 = 125,000 字节 + 8 字节开销。但根据分配策略,每个键可能有高达 250,000 + 8 字节的开销。

所以一些数学:

  • 每小时统计数据为 250 kb * 每天 24 = 6mb
  • 每天、每周和每月每个计数器 250kb(总共 1mb)

因此,在最坏的情况下,您仅在 4 天内就花费了 25 mb。但在最好的情况下,125 kb 是最大的密钥内存,您在 7-8 天内花费 25 mb。

BITSET 又快又好,但会消耗大量内存。如果您有很多在线用户,那么将 BITSET 用于每日、每周和每月数据似乎是一个正确的决定。 Bitset 不是稀疏数据的最佳选择。

分析您的每小时数据 - 可能正在使用 SET 允许使用更少的内存 - 您的 userId 是整数(4 个字节),因此 128kb 是每小时约 32,000 个用户。也看看 redis memory optimization guide - 你可能会发现很多关于 redis 中实际内存使用的有趣内容。

关于 SET 解决方法 - 您的每小时在线人数是否少于约 32,000 名用户 - SET 是您的选择。

您也可以使用 redis 命令DEBUG SDSLEN <key name> 调试您的 BITSET 键 - 它显示您为位集数据下的 SDS 字符串分配的内存。

【讨论】:

  • 这样很好,但是也有客户端太多的可能。每个客户端连接都会占用来自 Redis 中的最大内存设置的缓冲区的额外内存,因此在这种情况下,每个额外的客户端连接都会消耗 25MB 限制中的一部分。举例来说,如果每个连接占用 128k 的缓冲区空间并且有 100 个打开的连接,则几乎一半的可用内存被客户端占用。第三种可能性是缓慢的客户端通过保持缓冲区满来做同样的事情。
  • 据我所知,从 redis 来源,连接池每个连接不会消耗 128 kb。 Redis 是基于 epool 的,每个连接只消耗一些 kb。在客户端断开连接后,连接内存不会被缓存和释放,而不是键空间内存。所以我认为这不是马尔科姆问的这种行为的真正原因之一。你能告诉更多关于这个或者可能会发布一些关于这个的链接吗?
  • 128k 是驱动点而不是声明的示例。他们可以消耗的数量取决于缓冲区设置、客户端的速度和网络的速度。一些客户端库也没有正确关闭连接或为每个命令打开连接。最后,在尖峰之后是否向上馈送与连接打开时的内存状态无关。大多数客户端库正确地使连接保持打开状态。你需要看全局,而不仅仅是redis源。
  • 看起来像题外话。关于具体软件的问题 - redis 服务器。 Redis 使用 epool/select 作为网络后端。最后一个是有据可查的,并且可以预见地工作。此外,heroku 云平台不收取 linux 系统网络缓冲区分配的内存,所以我不明白你为什么评论。有时您需要在特定情况下回答特定问题,而不是想出一些假设的可能性。
  • Heroku 与此无关。这不是假设,它是很多人已经看到的,我已经看到发生在很多人身上。无论您是亲眼看到还是理解它,都不会改变事实。最初的问题缺乏足够的信息来做出正确的决定 - 并且是整个站点的主题(它属于 ServerFault)。您的回答虽然不错,但并不完整。这就是我所指的。您可以根据专家的反馈选择变得更好并改进答案以涵盖可能的情况,也可以拒绝,这取决于您。 EOR。
猜你喜欢
  • 2016-09-17
  • 1970-01-01
  • 2022-11-09
  • 2012-10-19
  • 2021-08-23
  • 2021-03-30
  • 2022-06-23
  • 2020-08-12
  • 2011-02-03
相关资源
最近更新 更多