php - MongoDB 功能测试设置和拆卸在 4.2 中使用 WiredTiger 慢 10 倍
问题描述
我正在将我们的 MongoDB 从 3.4(使用 MMAPv1 存储引擎)升级到 4.2(使用 WiredTiger)。在这一点上,我遇到的一件几乎是阻碍的事情是我们的测试严重放缓。
长话短说(下面有更多详细信息)- MongoDB 4.2 WiredTiger 需要更长的时间来处理测试中重复的数据库设置/拆卸。减速幅度大约为 10 倍。过去的测试运行大约 10 分钟,而 4.2 运行了将近 90 分钟。即使只进行了一小部分测试,这种减速也会重现,并且似乎来自测试的设置/拆卸阶段。
环境
关于我们的环境的几句话——我们正在使用 PHP 和 Doctrine ODM 来与 MongoDB 对话。我们有大约 3000 个测试,一些纯单元测试,一些(很多)功能,实际使用数据库。测试在 Dockerized 环境中运行——我们为每个管道启动了一个新的 MongoDB Docker 容器,但我已经确认即使在类似生产的裸机环境中也会出现同样的减速。下面的实验是在裸机上完成的,以限制来自其他地方的问题。
每个功能测试首先删除数据库,然后将夹具加载到其中(+ 创建索引),然后执行实际的测试场景。
分析 PHP
运行一小部分测试并测量时间,我得到以下结果:
3.4:
real 0m12.478s
user 0m7.054s
sys 0m2.247s
4.2:
real 0m56.669s
user 0m7.488s
sys 0m2.334s
如您所见,测试所用的实际 CPU 时间大致相同,没有显着差异。但是,实际时间非常不同,这表明需要等待很多时间(在这种情况下是等待 I/O 吗?)。
我进一步分析了 PHP 代码,从结果中可以看出,在这个函数上花费的时间增加了 9-10 倍:
MongoDB\Driver\Manager::executeWriteCommand()
该功能的文档说:
此方法将应用特定于写入命令的逻辑(例如 » drop)
这让我认为设置/拆卸的数量(即删除集合、创建索引)将在这里发挥作用。
分析 MongoDB
分析 PHP 指出了 MongoDB 的放缓,所以我也分析了这一点。我运行的测试子集导致
- 3.4 MMAPv1 的 1366 个分析文档
- 4.2 WiredTiger 的 2092 个分析文档
这些数字之间的大部分差异可归因于这样一个事实,即在 4.2 中没有文档createIndexes
(也许它们被添加到 3.4 后的分析中?我不知道)。
我过滤了分析文档,只显示那些花费至少 1 毫秒 (>0)的文档。曾经有:
- MongoDB 3.4 的 2 个此类文档(两个
drop
命令) - MongoDB 4.2 的 950 多个此类文档(209x
drop
、715xcreateIndexes
、4xinsert
、23xquery
)
正如我之前提到的,Mongo 3.4 似乎没有createIndexes
在分析中报告。但是让我们假设所有这些命令都将花费它们在 4.2 中的时间(尽管根据其余的分析结果,它们可能会花费更短的时间)。
然后在 4.2 中,所有这些drop
命令每次操作最多需要 15 毫秒。在 3.4 中也有 209 个drop
命令,但据报道几乎所有命令都持续了 0 毫秒。
只有最少量的插入和查询,并且当这些发生时集合的大小只有少数文档(每个集合少于 10 个,实际查询和插入的集合少于 5 个)。这种减速不是缺少缓存或索引的结果。在此设置中,即使是完整扫描也会很快。
内存和硬件
我发现的大多数关于此的讨论都是围绕为工作集设置适当的缓存大小。我在具有单核和 4GB RAM 的小型服务器上运行测试,默认缓存大小(应该是可用内存的 50%,即 2GB)。对于测试可能创建的所有数据来说,这绝对足够大。它们确实是微不足道的,花在它们上的大部分时间都花在了数据库状态的设置/拆卸上。
结论
这是我第一次分析我们的测试以及它们与数据库的交互。drop-and-index-creation 与实际工作的比率肯定可以提高,但到目前为止它已在 MMAPv1 和 MongoDB 3.4 上工作。这种类型的减速是 WiredTiger 所预期的吗?我能做些什么来减轻这种情况吗?
我现在害怕升级生产 MongoDB 实例,因为我不知道它们会如何表现。如果这主要与索引创建和数据库删除有关,那么我认为生产工作量应该没问题,但我不想冒险。遗憾的是,我们是一家相当小的公司,没有对生产环境进行任何性能/压力测试。
编辑
使用tmpfs
因为我在 Docker 中运行测试并且Docker 支持tmpfs
开箱即用的卷,所以我试了一下。当使用 RAM 支持tmpfs
作为 MongoDB 数据的挂载时,我设法将测试时间减少到大约一半:
4.2:
real 0m56.669s
user 0m7.488s
sys 0m2.334s
4.2 - tmpfs:
real 0m30.951s
user 0m7.697s
sys 0m2.279s
这更好,但仍与在 MMAPv1 上运行所需的 12 秒相去甚远。有趣的是,tmpfs
与 MMAPv1 一起使用并没有产生明显不同的结果。
测试放缓的真正原因 - 指数
事实证明,我们的测试框架和夹具加载器在每次数据库清除时为所有托管集合创建了索引。这导致每个测试用例创建了大约 100 个索引,这就是导致速度下降的原因。我没有直接从 Mongo 找到具体的证据,但使用 WiredTiger 创建索引似乎比使用MMAPv1慢得多。从测试设置代码中删除索引创建显着加快了测试速度,让我们回到了升级前的时间。
我们的绝大多数测试都不需要索引,它们的创建时间比它们提供的查询加速时间要长得多。我实现了一个选项来强制为开发人员知道他们需要它们的测试用例创建索引。这对我们来说是一个可以接受的解决方案。
解决方案
将数据库的数据放入内存。在 Linux 上,我推荐zram。
根据我的经验,zram 在 raid 0 中的速度是顶级 nvme ssd(我认为是三星 860 pro)的 2 倍,并且我认为几乎是单个消费级笔记本电脑 SSD 的 10 倍。对于通过网络访问的旋转磁盘或存储,差异应该更大。
MongoDB 有各种其他存储引擎(我相信有一个称为“用于测试的临时”),但它们不支持事务,因此如果您的应用程序使用 4.2(甚至我认为是 4.0)功能,则需要使用 WT。
在生产中,您很可能不会在每个请求中都删除集合,因此 3.x 和 4.2 之间的实际性能差异应该更小。
推荐阅读
- javascript - `finally` 中的 return 语句覆盖 `try` 中的 return 语句
- r - 观星者错误“无法更改 NULL 的长度”
- angular - 在 Linux Web 应用程序上的 Azure 中运行 Angular
- selenium - 为什么 google 搜索页面源结果会导致 webdriver 崩溃?
- php - 使用带有模板部分的 index.php 从 wordpress 中排除重复的帖子
- javascript - 如何使用 io-ts 验证数组长度?
- php - 循环遍历关联数组键
- javascript - 我正在努力确定选择了哪个下拉元素
- c# - 如何在c#中使用进程类?
- angular - 页面初始加载时显示两个菜单