MongoDB 和无论如何推挤都无法放入 RAM 的数据集

MongoDB 和无论如何推挤都无法放入 RAM 的数据集

这非常依赖于系统,但几乎可以肯定我们会越过某个任意的悬崖并陷入真正的麻烦。我很好奇,对于良好的 RAM 与磁盘空间比率,存在什么样的经验法则。我们正在规划下一轮系统,需要对 RAM、SSD 以及新节点将获得多少做出一些选择。

现在来看一下一些性能细节!

在单个项目运行的正常工作流中,MongoDB 的写入百分比非常高(70-80%)。一旦处理管道的第二阶段开始,它的读取率就会非常高,因为它需要对在处理的第一半部分中识别的记录进行重复数据删除。这就是“将工作集保存在 RAM 中”的工作流,我们正在围绕这一假设进行设计。

整个数据集不断受到来自最终用户来源的随机查询的影响;尽管频率不规则,但规模通常很小(10 个文档组)。由于这是面向用户的,因此回复需要低于 3 秒的“无聊”阈值。这种访问模式不太可能出现在缓存中,因此很可能会产生磁盘命中。

二级处理工作流程需要大量读取之前几天、几周甚至几个月前的处理运行,虽然运行频率不高,但仍然需要快速运行。之前处理运行中多达 100% 的文档将被访问。我怀疑,无论多少缓存预热都无法解决这个问题。

完成的文件大小差异很大,但中位数大小大约是8K。

正常项目处理中的高读取部分强烈建议使用副本来帮助分配读取流量。我已阅读别处对于慢速磁盘来说,1:10 的 RAM-GB 与 HD-GB 是一个很好的经验法则,由于我们正在认真考虑使用速度更快的 SSD,我想知道对于快速磁盘是否也有类似的经验法则。

我知道我们使用 Mongo 的方式不适合缓存一切,这就是为什么我正在寻找方法来设计一个可以承受这种使用方式的系统。全部的数据集很可能在半年内就达到 TB 左右,并且还在不断增长。

答案1

这将是一堆小问题。然而遗憾的是,你的问题没有单一的答案。

MongoDB 允许操作系统内核处理内存管理。除了投入尽可能多的 RAM 来解决问题外,只有少数方法可以“主动管理”您的工作集。

为优化写入,您可以做的一件事是首先查询该记录(执行读取),以便它位于工作内存中。这将避免与进程范围的全局锁(在 v2.2 中应该变为每个数据库)相关的性能问题

RAM 与 SSD 的比例没有硬性规定,但我认为 SSD 的原始 IOPS 应该允许您采用低得多的比例。在我看来,1:3 可能是您想要的最低比例。但考虑到更高的成本和更低的容量,您可能还是需要降低该比例。

关于“写入与读取阶段”,我是否正确地理解了,一旦记录被写入,它就很少被更新(“更新插入”)?如果是这样,可能值得托管两个集群;正常的写入集群和读取优化集群,用于尚未修改的“老化”数据[X时间段]。我肯定会在此集群上启用从属读取。(就我个人而言,我会通过在数据库的对象文档中包含修改日期值来管理它。)

如果您有能力在进入生产环境之前进行负载测试,请对其进行性能监控。MongoDB 的编写假设是它经常部署在虚拟机中(它们的参考系统在 EC2 中),因此不要害怕将其分片到虚拟机中。

答案2

本文旨在作为此处发布的其他答案的附录,这些答案讨论了许多要在此处考虑的相关元素。然而,在随机访问型系统中,还有一个经常被忽视的因素,即预读,可以提高 RAM 的利用率。

您可以通过运行(通常需要 sudo/root 权限)来检查 readahead 的当前设置(在 Linux 上)blockdev --report。这将打印出一个表格,每个磁盘设备占一行。RA 列包含 readahead 的值。该值是每次访问磁盘时读取的 512 字节扇区的数量(除非扇区大小不是默认值 - 请注意,截至撰写本文时,即使磁盘大小较大,内核也会将其视为 512 字节扇区)。

您可以通过运行以下命令来设置给定磁盘设备的预读设置:

blockdev --setra <value> <device name>

当使用基于软件的 RAID 系统时,请确保在每个磁盘设备以及与 RAID 控制器相对应的设备上设置预读。

为什么这很重要?因为 readahead 使用的资源与 MongoDB 尝试使用的资源相同,目的是优化顺序访问读取 - RAM。当您在旋转磁盘(或任何类似旋转磁盘的设备 - EBS,我正在研究您)上执行顺序读取时,将附近的数据提取到 RAM 中可以大大提高性能,节省您的寻道时间,并且在正确的环境中将 readahead 设置得较高可以为您带来一些令人印象深刻的结果。

对于像 MongoDB 这样的系统,您的访问通常是对数据集的随机访问,这只会浪费内存,而这些内存本可以用在其他地方。正如其他地方提到的,该系统还管理 MongoDB 的内存,当请求时,它将分配一块内存用于预读,因此留给 MongoDB 有效使用的 RAM 更少。

选择正确的预读大小很棘手,取决于您的硬件、配置、块大小、条带大小和数据本身。例如,如果您确实要使用 SSD,则需要较低的设置,但设置多低取决于数据。

解释一下:您要确保预读足够高,以便提取完整的单个文档,而不必返回磁盘。我们以您提到的中位数 8k 为例 - 由于磁盘上的扇区通常为 512 字节,因此需要 16 次磁盘访问才能读取整个文档,而无需预读。如果您的预读有 16 个或更多扇区,则只需一次磁盘访问即可读取整个文档。

实际上,由于 MongoDB 索引存储桶为 8k,您无论如何都不想将预读设置为低于 16,否则将需要 2 次磁盘访问才能读取一个索引存储桶。一般来说,一个好的做法是从当前设置开始,将其减半,然后重新评估您的 RAM 利用率和 IO,然后从那里继续。

答案3

您应该考虑使用副本来处理最终用户查询,并在其他机器上完成您的工作流程。

使用 1:10 的经验法则,您需要大约 128GB 的​​ RAM 才能拥有 1TB 的磁盘存储;尽管如今一些价格实惠的 SSD 声称可以达到 >60K IOPS,但实际数字可能有很大差异,也取决于您是否在 SSD 中使用 RAID,如果使用,那么 RAID 卡也非常重要。

在撰写本文时,在 1U Intel 服务器上,从 128GB DDR3 ECC 内存升级到 256GB 似乎需要额外花费 2000 美元左右,这将为您提供 1TB 数据的 1:5 比率,我认为这会是一个更好的比率。如果您需要尽快完成工作量,那么更多的内存肯定会有所帮助,但真的那么紧急吗?

您还需要做一些文件系统调整,例如在 ext4 上执行“noatime,data=writeback,nobarrier”,并且您可能还需要做一些内核设置调整,以从您的系统中获得最高的性能。

如果您要使用 RAID,RAID-10 将是一个不错的选择,使用合适的 RAID 控制器将提供相当大的性能提升,但可用空间会减半。如果您希望在不将可用空间减半的情况下获得相当大的性能提升,您也可以考虑 RAID50。运行 RAID 的风险是您不再有权访问驱动器上的 TRIM,这意味着您需要不时地将数据移出、拆分 RAID、对驱动器进行 TRIM 并重新创建 RAID。

最终,您需要决定您想要的复杂程度、您想要花费多少钱以及您希望工作负载处理的速度。我还会评估 MongoDB 是否是理想的数据库,因为您仍然可以使用 Mongo 来处理需要快速响应的最终用户查询,但使用其他东西来处理您的数据,这些数据不需要在几秒钟内准备好,它也可能允许您更轻松地将工作负载分散到多台机器上。

相关内容