MongoDB使用第二个索引插入性能

时间:2015-05-26 17:51:50

标签: performance mongodb indexing bulkinsert

我正在尝试使用WiredTiger将大约2.5亿个文档(每个大约400个字节)插入到MongoDB 3.0中。我只需搜索一个短字符串键_user_lower。虽然我现在正在使用WiredTiger,这比MMAPv1要好得多,但我确实首先使用了MMAPv1并且遇到了类似的问题。

我的服务器(非常便宜的VPS)有:

  • 250 GB磁盘
  • 1 GB RAM
  • 2 GB交换
  • 2.1 GHz单核CPU

我知道这台机器真的很慢,我要求它做一些不切实际的事情。但我对它如何以一个索引开始这么快感到困惑,而第二个只是破坏了性能:

我插入了当时(大约250M行)的所有数据,没有任何索引,_id 除外。考虑到我糟糕的硬件,这表现非常好:

  • 每秒约 5000次插入(完全可以接受)
  • 这个速度在完成所需的14小时内几乎不变
  • _id完成后的索引大小接近2.5GB。请注意,这是物理RAM的两倍以上。
  • 根据mongostat,该过程的RES不超过450 MB。
  • 没有交换
  • top似乎表明CPU时间并非全部用于等待磁盘(因此在用户空间中花费了大量资金,可能是snappy代码中的WiredTiger)

然后我在我需要查询的唯一字段_user_lower上构建了一个(非唯一)索引。这花了7.7个小时,这很好,因为这是一次性交易。该索引最终为1.6 GB,与_id索引相比,这对我来说似乎很低。 RES上升到大约750 MB。

然后,我下载了一个要加载的新数据集。它只有 102 MB(238 K文件)。我使用mongoimport以相同的方式加载它,但这一次:

  • 每秒仅 80次插入(有时慢)
  • RES保持在750 MB左右
  • top表示几乎100%的CPU用于等待IO
  • 当然,负载经过屋顶。

我可以理解相当大的性能损失,因为该索引必须更新。但我没想到这么多。我已经阅读了我的索引应该适合RAM的所有地方,但是在初始插入期间性能很好,索引很快就超出了我的记忆。

我可以优化_user_index索引吗?我不知道这甚至意味着什么,但也许只能索引前几个字符?我绝对愿意将查询性能减半,以换取插入性能增加三倍。

对于大规模性能影响的原因是什么?如何在没有新硬件的情况下修复它?我并没有真正依赖MongoDB,所以没有这些性能特征的替代品都可以。我有一个想法,只使用平面文件可能会工作,但我不想写所有的代码。

1 个答案:

答案 0 :(得分:3)

将新项目添加到集合时,数据库必须使索引保持最新。由于默认情况下MongoDB中的索引是B树,这意味着它必须在树中插入一个项目。虽然在最好的情况下这不是特别昂贵的操作,但它带来两个潜在的性能问题:

  1. 性能抖动:不时,B-Tree存储桶可能已满,需要分割桶,因此比“简单”插入更多操作
  2. 插入目的地必须随时可用
  3. 在这种情况下,后者很可能会引起麻烦:因为名称的插入会触及树中的随机节点(即,名称插入不遵循模式)并且您的RAM小于索引,必须从磁盘获取目标的可能性很高。不幸的是,磁盘搜索的性能是orders of magnitude lower than main memory references。如果你运气不好,第一个ref位置需要另一个磁盘搜索,以便在MongoDB甚至开始写入之前,对于单个插入需要多个磁盘读取。这可能需要数百毫秒,旋转磁盘或典型IaaS基础设施上的争用甚至几秒钟。

    因为ObjectIds是单调生成的(时间戳是最重要的部分),所以插入总是在最后发生,并且可以将目标保持在RAM中。性能抖动,即问题1可能仍然是一个问题,因为存储桶拆分可能需要磁盘搜索,但与第一种情况相比,它很少发生,因为它不会破坏平均性能,这应该可以解释观察到的行为。

    此外,当桶以单调递增的值填充时,MongoDB将在90%填充时拆分桶;随机插入,分裂将在50%之前发生很多,因此在这种情况下树更加“密集”。