【问题标题】:Performance scaling in MongoDB (search operations)?MongoDB 中的性能扩展(搜索操作)?
【发布时间】:2014-06-26 10:58:28
【问题描述】:

我正在尝试将 mongoDB 用于大量文档。只要数据更小并且适合内存,一切都可以正常工作,但是如果不适合,则读取/搜索性能很糟糕。对于 5 亿个文档集合,一个简单的索引查询(例如,对单个索引字段的正则表达式查询)运行近 2 小时。这很奇怪 - 集合大小约为。 300GB,从硬盘顺序读取应该不超过半小时。而且由于它使用索引,它应该更快(结果有大约 100k 个文档;集合中这个特定字段的不同值大约有 2M)。如果在(不同的)索引键上完成,单独的正则表达式匹配应该不超过几秒钟 - 匹配这些值的简单 python 脚本需要 6 秒。

据我所知,即使在使用此类索引处理查询(类似查询但没有结果)时,DBMS 也会以随机顺序访问磁盘。我使用了许多索引,它们都不能同时放入内存,所以这似乎是问题所在(它们使用 50-60GB,机器有 32GB 的 RAM)。因此,问题是:是否可以调整 DBMS I/O 方法以执行更智能的预读或操作排序?

或者也许我应该切换到 mongo 以外的东西?到目前为止,我已经看过 Lucene 和 Cassandra,我认为它们不符合我的需求。我的任务更详细:

我想使用DB根据对属性的一些要求从大量的小属性结构中选择文档。这些结构有时是嵌套的。我只需要检索它们的标识符以进行进一步处理,但所有属性/字段都用于搜索。我需要的操作是单个字段上条件的连接和替代条件,其中条件是:等于常量,正则表达式匹配(不是全文),加上子结构列表上的量词(列表上的所有子结构都有一个属性匹配正则表达式.. .,存在一个属性等于...的子结构,依此类推)。

典型的使用是并发只读操作;写入是不频繁的批量插入(独占 - 只写)。 500M 的集合是我的开发/测试数据,生产将使用多达 10G 的文档,10TB 的数据(这就是为什么像“购买更多 RAM”这样的任何建议都无济于事的原因)。

感谢您的帮助, 巴特

编辑:

.explain() 输出:

{
"cursor" : "BtreeCursor orth_1",
"isMultiKey" : false,
"n" : 107290,
"nscannedObjects" : 107290,
"nscanned" : 250202122,
"nscannedObjectsAllPlans" : 107290,
"nscannedAllPlans" : 250202122,
"scanAndOrder" : false,
"indexOnly" : false,
"nYields" : 2954078,
"nChunkSkips" : 0,
"millis" : 6193156,
"indexBounds" : {
    "orth" : [
        [
            "",
            {

            }
        ],
        [
            /[A-Z].+ski/,
            /[A-Z].+ski/
        ]
    ]
},
"server" : "bart:27017",
"filterSet" : false
}

日志条目:

2014-06-26T11:46:41.672+0200 [conn2] query nkjp_300m_noni.simple_nodes query: { query: { orth: /[A-Z].+ski/ }, $explain: true } planSummary: IXSCAN { orth: 1.0 } ntoreturn:0 ntoskip:0 nscanned:250202122 nscannedObjects:107290 keyUpdates:0 numYields:1016206 locks(micros) r:355736129 nreturned:1 reslen:1187 6193156ms

编辑2: 再次运行相同的查询,中间没有任何其他查询需要 130 秒。

【问题讨论】:

  • 你应该提供一些调试信息。我认为explain() 输出将是理想的。另外,你确定 mongo 在这 2 个小时内做了什么吗?有没有我们可以使用的监控数据?日志条目?当时的系统资源如何(mem 使用情况、io 等)?
  • 我正在附加 .explain() 输出和日志条目。 Mongo 主要等待,CPU 使用率约为 1%,磁盘读取速度约为 1MB/s,内存几乎已满缓冲区/缓存。
  • 你能告诉我们实际的查询吗?
  • 这里是:db.simple_nodes.find({'orth' : /[A-Z].+ski/}).explain()
  • 总是/[A-Z].+ski//^[A-Z].+ski//[A-Z].+ski$/ 吗?

标签: mongodb


【解决方案1】:

我将首先仔细查看您的 explain() 输出中的这三行:

"n" : 107290,
"nscannedObjects" : 107290,
"nscanned" : 250202122,

您的查询必须查看超过 250M 的索引条目才能检索 107290 个文档。这是正则表达式查询对索引的使用相对较差的功能。正则表达式查询一开始就不是很好的索引用户,而且没有前缀表达式(例如{ orth: /^A.+ski/ })。来自文档:

如果字段存在索引,则 MongoDB 匹配正则 针对索引中的值的表达式,这可能比 收藏扫描。如果常规的可以进一步优化 表达式是一个“前缀表达式”,这意味着所有潜在的 匹配以相同的字符串开头。这允许 MongoDB 构建一个 来自该前缀的“范围”,并且仅匹配来自该前缀的那些值 范围内的索引。

如果正则表达式以 a 开头,则它是“前缀表达式” 插入符号 (^) 或左锚 (\A),后跟一串简单的 符号。例如,正则表达式 /^abc.*/ 将通过匹配来优化 仅针对以 abc 开头的索引中的值。

此外,虽然 /^a/、/^a./ 和 /^a.$/ 匹配等价物 弦,它们具有不同的性能特征。所有这些 如果存在适当的索引,则表达式使用索引;然而, /^a./ 和 /^a.$/ 较慢。 /^a/ 匹配后可以停止扫描 前缀。

如果该索引无法放入内存,则搜索将依赖于将索引分页进出内存,这将非常慢。这个问题(您需要内存中的完整索引,而您没有足够的内存用于索引和工作集)确实会影响 MongoDB 的性能。

您的选择有限:

  • 添加更多内存 - MongoDB 性能将严重下降 只要你不能适应你的工作集加上关键的影响 内存中的索引片段。

  • 重构查询以使用前缀表达式 - 来自 cmets I 看到这可能不是一个选项,但我将其包含在内以完成。

  • 移动到 SSD 或其他快速磁盘选项 - 如果您无法获得足够的 内存,您需要从磁盘中提取内容,SSD 通常会有所帮助。 同样,考虑到整体数据库大小,这对您来说可能不可行 但这里是为了完整性。

  • 重构以使用文本索引 - 这将需要更多的分析和 更深入地了解您的应用程序、架构和使用情况 模式,但值得一看。文本索引具有不同的优势和 比对常规索引的正则表达式查询的弱点,并且不是魔术 子弹。鉴于您现有的用途,我怀疑这可能对您不起作用 正则表达式查询,但值得进行一些实验。

  • 将搜索移至专用文本索引软件包 - 个人 我会仔细研究使用 Solr 或 Elasticsearch 作为您的文本搜索 器具。有强大的软件包可以让您的 与您的 MongoDB 同步的 Elasticsearch 文本索引(请参阅 https://github.com/richardwilly98/elasticsearch-river-mongodb/)。您的 高读取、低批量写入场景非常适合这种方法 并且您可以使用 Elasticsearch 进行更灵活的调优 您的字段定义和查询以获得更好的文本搜索 表现。横向扩展也相当简单。

【讨论】:

    【解决方案2】:

    根据regex documentation,如果正则表达式是前缀表达式,则索引可以更好地执行正则表达式搜索。我想你可以试试这个。

    还有text-index,但我不确定它是否符合您的目的。

    如果这些都没有帮助,您是否可以尝试在写入时操作数据?我在想,如果您的查询集非常有限并且已知,您可以在插入记录时在记录中添加一些标志数据,然后在它们上添加索引。

    【讨论】:

    • 模式和其他条件直接来自用户,这是一个非常常见的例子。标志不会有帮助,但我会尝试将这些属性的所有不同值存储在一边,然后在那里匹配正则表达式并在主集合中使用一些引用。但我仍然认为模式匹配不是我的瓶颈......
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-04
    • 2019-11-11
    • 1970-01-01
    相关资源
    最近更新 更多