我有一个集群应用程序,它分为一个控制器和一堆工作人员。控制器在专用主机上运行,工作人员通过网络拨打电话并获得递交工作,这是正常的。 (基本上是zeromq手册中的“divide-and-conquer pipeline”,有特定工作的皱纹。现在这并不重要。)
控制器的核心数据结构是伪C ++中的unordered_map<string, queue<string>>
(控制器实际上是用Python实现的,但我很乐意用其他东西重写它)。队列中的字符串定义作业,而映射的键是作业的分类。控制器播种一组作业;当一个工人启动时,控制器从其中一个队列中删除一个字符串,并将其作为工作人员的第一份工作交出。工作人员可能会在运行期间崩溃,在这种情况下,作业将被放回适当的队列(有一个未完成工作的辅助表)。如果成功完成作业,它将发回一个新作业字符串列表,控制器将对其进行排序。然后它将从某个队列中拉出另一个字符串并将其作为下一个作业发送给工作人员;通常,但并非总是如此,它将为该工作人员选择与上一个工作相同的队列。
现在,问题。这个数据结构目前完全位于主内存中,这对于小规模的测试运行来说很好,但是在满量程下正在吃控制器上所有可用的RAM,这一切都是自己的。控制器还有其他几项要完成的任务,所以这并不好。
我应该采取什么方法?到目前为止,我已经考虑过:
a)将其转换为主要在磁盘上的数据结构。它可以在某种程度上缓存在RAM中以提高效率,但是工作需要几十秒才能完成,所以如果 有效,那就没关系,
b)使用关系数据库 - 例如SQLite,(但SQL模式非常适合AFAICT), c)使用具有持久性支持的NoSQL数据库,例如: Redis(数据结构映射过程非常简单,但这仍然以RAM为中心,让我确信内存耗尽的问题实际上会消失)具体数字:对于全面运行,哈希中将有一千到一千万个密钥,每个队列中的密钥少于100个。字符串长度变化很大,但不可能超过250-areh字节。因此,假设的(不可能的)零开销数据结构将需要2个 34 - 2 37 字节的存储空间。
答案 0 :(得分:0)
最终,这一切都归结为如何定义部分控制器所需的效率 - 例如响应时间,吞吐量,内存消耗,磁盘消耗,可伸缩性......这些属性直接或间接与以下内容相关:
根据您的选择,以下是我评估每个选项的方式:
a)将其转换为主要在磁盘上的数据结构。它可能是 在某种程度上缓存在RAM中以提高效率,但是工作需要数十次 几秒钟完成,所以没关系,如果效率不高,
考虑到当前的内存占用需求,某种形式的持久存储似乎是一种可靠的选择。如果存在可重复的访问模式,缓存就会发挥作用,比如一遍又一遍地访问相同的队列 - 否则,缓存可能没有帮助。
如果1)您找不到一个简单地映射到您的数据结构的数据库(不太可能),则此选项是有意义的,2)由于某些其他原因您希望拥有自己的磁盘格式,例如:你发现转换到数据库的开销太大(再次,不太可能)。
数据库的一种替代方法是查看持久性队列(例如,使用RabbitMQ后备存储),但我不确定每个队列或整体大小限制是什么。
b)使用关系数据库 - 例如SQLite,(但SQL模式是一个 非常不适合AFAICT),
正如您所提到的,SQL可能不太适合您的要求,即使您可以确定将数据结构映射到关系模型以某种方式。
然而,像MongoDB或CouchDB这样的NoSQL数据库似乎更合适。无论哪种方式,只要能够满足您的吞吐量要求,某种数据库似乎都是可行的。从可扩展性的角度来看,许多(如果不是大多数)NoSQL数据库也是一个不错的选择,因为它们包括跨多台机器支持sharding数据。
c)使用具有持久性支持的NoSQL数据库,例如: Redis(数据 结构映射过于平凡,但这仍然看起来非常以RAM为中心 让我相信记忆猪的问题会真正发生 远)
像Redis这样的内存数据库无法解决内存耗尽问题,除非您设置了一组机器,每台机器都包含整个数据的一部分。仅当由于低响应时间要求而需要保留所有内存数据时才有意义。然而,考虑到你的工作性质,需要几十秒才能完成,响应时间与工人相应,几乎不重要。
但是,如果您发现响应时间很重要,Redis将是一个不错的选择,因为它可以使用客户端一致性散列或集群级别轻松处理partitioning,因此也支持可扩展性方案
无论如何
在选择解决方案之前,请务必澄清您的要求。你提到你想要一个高效的解决方案。由于效率只能根据一些要求来衡量,所以我将首先尝试回答的问题清单:
* 要求的
从中得出结论:
展望未来:
再次,从中得出结论,
通过这些答案,您将发现自己处于更适合选择解决方案的位置。
答案 1 :(得分:0)
我会查看像RabbitMQ这样的消息队列。这样它将首先填满RAM然后使用磁盘。我在一台服务器上的队列中有多达500,000,000个对象,它只是插件。
RabbitMQ适用于Windows和Linux,并且具有简单的连接器/ SDK,适用于任何类型的语言。