【问题标题】:Growing hash-of-queues beyond main memory limits超出主内存限制的队列散列增长
【发布时间】:2014-02-09 23:16:51
【问题描述】:

我有一个集群应用程序,它分为一个控制器和一堆工人。控制器在专用主机上运行,​​工作人员通过网络打电话并接手工作,到目前为止一切正常。 (基本上是 zeromq 手册中的“divide-and-conquer pipeline”,带有特定于工作的皱纹。现在这并不重要。)

控制器的核心数据结构是伪 C++ 中的unordered_map<string, queue<string>>(控制器实际上是用 Python 实现的,但我对用其他东西重写它的可能性持开放态度)。队列中的字符串定义作业,映射的键是作业的分类。控制器播种了一组作业;当工作人员启动时,控制器从其中一个队列中删除一个字符串并将其作为工作人员的第一项工作分发。工作人员可能会在运行期间崩溃,在这种情况下,作业会被放回适当的队列中(有一个未完成作业的辅助表)。如果它成功完成了作业,它会发回一个新的作业字符串列表,控制器将把它分类到适当的队列中。然后它将从某个队列中拉出另一个字符串并将其作为下一个作业发送给工作人员;通常,但并非总是如此,它会为该工作人员选择与上一个作业相同的队列。

现在,问题。此数据结构目前完全位于主内存中,这对于小规模测试运行来说很好,但在全规模时,它会独自吃掉控制器上所有可用的 RAM。控制器还有其他几项任务要完成,所以这不好。

我应该采取什么方法?到目前为止,我已经考虑过:

a) 将其转换为主要在磁盘上的数据结构。它可以在一定程度上缓存在 RAM 中以提高效率,但作业需要数十秒才能完成,所以如果它没有那么效率也没关系,

b) 使用关系数据库 - 例如SQLite,(但 SQL 模式非常不适合 AFAICT),

c) 使用具有持久性支持的 NoSQL 数据库,例如Redis(数据结构映射微不足道,但这看起来仍然非常以 RAM 为中心,让我确信内存占用问题实际上会消失)

具体数字:对于全面运行,散列中将有 1 到 1000 万个键,每个队列中的条目少于 100 个。字符串长度变化很大,但不太可能超过 250 个字节。因此,假设的(不可能的)零开销数据结构将需要 234 – 237 字节的存储空间。

【问题讨论】:

  • 虽然这个问题很有趣,但您可能需要重新表述它。如您所知,要求图书馆的问题并不适合 SO!此外,一些数字会很有用。例如,哈希表中有多少项?
  • @Shahbaz 我知道这样的问题是不鼓励的,但在这种情况下,我无法找到更好的场所或更好的方式来构建问题。扩展它(“我应该对这个数据结构做些什么,以便它更适合内存和/或主要存在于磁盘上?”也许)只会使它更多成为一个见仁见智的问题,我认为。我会在正文中添加一些具体的数字。
  • @zack 稍作编辑以避免偏离主题的讨论。
  • 使所有内容都固定大小,超大哈希表和 mmap() 每次请求都会花费 2-3 个页面错误。参考的局部性会很糟糕(除非在少数热点上进行大量操作)。只需计算一下:你能承受几百 GB 的 VM 占用空间吗?另外:你想要持久性,即使在崩溃之后?
  • @zack 你能澄清一下你需要在多少时间(例如每小时)内处理多少工作?你为什么一开始就把所有的东西都放在内存中?

标签: data-structures language-agnostic scalability


【解决方案1】:

最终,这一切都归结为您如何定义部分控制器所需的效率——例如响应时间、吞吐量、内存消耗、磁盘消耗、可扩展性……这些属性直接或间接与:

  1. 控制器每秒需要处理的请求数(吞吐量)
  2. 可接受的响应时间
  3. 未来增长预期

根据您的选择,我将如何评估每个选项:

a) 将其转换为主要在磁盘上的数据结构。它可能是 在一定程度上缓存在 RAM 中以提高效率,但作业需要数十 秒完成,所以效率不高也没关系,

鉴于当前的内存占用需求,某种形式的持久存储似乎是一个合理的选择。如果存在可重复的访问模式,例如反复访问同一个队列,缓存就会发挥作用——否则,缓存可能无济于事。

如果 1) 您找不到与您的数据结构无关的数据库(不太可能),2) 出于某些其他原因,您希望拥有自己的磁盘格式,则此选项是有意义的,例如您发现转换为数据库的开销太大(同样,不太可能)。

数据库的一种替代方法是查看持久队列(例如,使用 RabbitMQ 后备存储),但我不确定每个队列或总体大小限制是多少。

b) 使用关系数据库 - 例如SQLite,(但 SQL 模式是 非常不适合 AFAICT),

正如您所提到的,SQL 可能不适合您的要求,即使您确实可以将您的数据结构映射到关系模型以某种方式

然而,像 MongoDB 或 CouchDB 这样的 NoSQL 数据库似乎更合适。无论哪种方式,只要能够满足您的吞吐量要求,某种数据库似乎都是可行的。从可扩展性的角度来看,许多(如果不是大多数的话)NoSQL 数据库也是不错的选择,因为它们支持跨多台机器的 sharding 数据。

c) 使用具有持久性支持的 NoSQL 数据库,例如Redis(数据 结构映射很简单,但这仍然显得非常以 RAM 为中心 让我有信心记忆力问题会真正解决 离开)

像 Redis 这样的内存数据库并不能解决内存占用问题,除非您设置一个机器集群,每台机器都保存一部分整体数据。仅当由于 低响应时间 要求而需要将所有数据保存在内存中时,这才有意义。然而,考虑到您的工作性质,需要数十秒才能完成,因此对于工作人员而言,响应时间并不重要。

但是,如果您发现响应时间确实很重要,那么 Redis 将是一个不错的选择,因为它使用客户端一致性哈希或在集群级别轻松处理 partitioning,因此还支持可扩展性场景。

无论如何

在选择解决方案之前,请务必明确您的要求。你提到你想要一个高效的解决方案。由于只能根据某些要求来衡量效率,因此我将首先尝试回答的问题列表如下:

*要求

  • 预计每分钟或每小时完成多少作业?
  • 需要多少工人才能做到这一点?

由此得出结论:

  • 每秒请求数的预期负载是多少,以及
  • 控制器部分的预期响应时间(分发作业、接收结果)?

展望未来:

  • 工作负载是否会增加,即您的解决方案是否需要扩展(每个时间单位的作业更多,每个作业的数据更多?)
  • 是否需要工作和结果的持久性,例如用于审计目的?

再次,总结,

  • 这将如何影响工人的数量?
  • 它对部分控制器每秒的请求数有什么影响?

有了这些答案,您会发现自己可以更好地选择解决方案。

【讨论】:

    【解决方案2】:

    我会研究像 RabbitMQ 这样的消息队列。这样,它将首先填满 RAM,然后使用磁盘。我在单个服务器上的队列中最多有 500,000,000 个对象,而且它只是被堵住了。

    RabbitMQ 可在 Windows 和 Linux 上运行,并且具有简单的连接器/SDK,可连接任何类型的语言。

    https://www.rabbitmq.com/

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-04-26
      • 1970-01-01
      • 2014-08-08
      • 2019-12-14
      • 2020-05-13
      • 1970-01-01
      • 2018-08-01
      相关资源
      最近更新 更多