【发布时间】:2020-07-16 17:17:22
【问题描述】:
我正在将我们的 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()
该函数的documentation 表示:
此方法将应用特定于写入命令的逻辑(例如 » drop)
这让我认为设置/拆卸的数量(即删除集合、创建索引)将在这里发挥作用。
分析 MongoDB
分析 PHP 指出了 MongoDB 的放缓,所以我也分析了这一点。我运行的测试子集导致
- 3.4 MMAPv1 的 1366 个分析文档
- 4.2 WiredTiger 的 2092 个分析文档
这些数字之间的大部分差异可归因于在 4.2 中没有createIndexes 的文档(也许它们被添加到 3.4 后的分析中?我不知道)。
我过滤了分析文档以仅显示那些花费至少 1 毫秒 (>0) 的文档。有:
- 2 个这样的 MongoDB 3.4 文档(两个
drop命令) - 950+ MongoDB 4.2 的此类文档(209x
drop,715xcreateIndexes,4xinsert,23xquery)
正如我之前提到的,Mongo 3.4 似乎没有在分析中报告createIndexes。但是让我们假设所有这些命令都将花费它们在 4.2 中的时间(不过,根据其余的分析结果,它们可能会花费更短的时间)。
然后是所有那些drop 命令,在 4.2 中每个操作最多需要 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 supports tmpfs volumes out-of-the-box 中运行测试,所以我试了一下。当使用 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 慢。从测试设置代码中删除索引创建显着加快了测试速度,让我们回到了升级前的时间。
我们的绝大多数测试都不需要索引,而且它们的创建时间比它们提供的查询加速时间要长得多。我实现了一个选项来强制为开发人员知道他们需要它们的测试用例创建索引。这对我们来说是一个可以接受的解决方案。
【问题讨论】:
-
在 3.4 和 4.0 之间有 很多 的变化。您可能需要分别测试各种操作,看看哪些操作变慢了,然后才开始调查原因。
标签: php mongodb doctrine doctrine-odm wiredtiger