MongoDB中的性能扩展(搜索操作)?

时间:2014-06-26 10:58:28

标签: mongodb

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

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

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

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

典型用法是并发只读操作;写入很少,批量插入(独占 - 只写)。 500M系列是我的开发/测试数据,生产将使用最多10G的文件,10TB的数据(这就是为什么任何类似于购买更多内存的消息并没有帮助)。

感谢您的帮助, 巴特

编辑:

.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

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

2 个答案:

答案 0 :(得分:1)

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

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

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

  

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

     

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

     

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

如果该索引无法适应内存,则搜索将依赖于对内存中的索引进行分页,这将非常慢。这个问题(你需要内存中的完整索引,而你没有足够的内存用于索引和工作集)真的会杀死MongoDB的性能。

您的选择有限:

  • 添加更多内存 - MongoDB性能将会严重下降 只要你不能适应你的工作组加上批评就会受到影响 内存中的索引部分。

  • 重新考虑您的查询以使用前缀表达式 - 来自评论I 看到这可能不是一个选择,但我把它包括在内是完整的。

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

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

  • 将搜索内容移至专用的文本索引软件包 我会仔细研究使用Solr或Elasticsearch作为文本搜索 器具。有坚固的套餐可以保持你的 Elasticsearch文本索引与MongoDB同步(请参阅 https://github.com/richardwilly98/elasticsearch-river-mongodb/)。您的 高读取,低批量写入方案非常适合这种方法 并且您可以通过Elasticsearch进行调整,从而获得更大的灵活性 您的字段定义和查询以获得更好的文本搜索 性能。这也是相当直接的扩展。

答案 1 :(得分:0)

如果正则表达式是前缀表达式,根据regex documentation索引可以更好地执行正则表达式搜索。我想你可以为你的情况尝试一下。

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

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