MongoDB分页获取数据排序阶段缓存溢出问题

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MongoDB分页获取数据排序阶段缓存溢出问题相关的知识,希望对你有一定的参考价值。

参考技术A

查询语法如下:

报错信息如下:

1、 扩大排序内存的限制,例如扩大10倍至320M。如:

2、 给排序字段加索引。如:

3、 在执行一个更大规模排序时,即使已经加了索引依然超过限制,可以使用aggregate()方法的 allowDiskUse 参数设置将数据写到临时文件进行排序。如:

文本搜索中的 MongoDB 溢出排序阶段

【中文标题】文本搜索中的 MongoDB 溢出排序阶段【英文标题】:MongoDB Overflow sort stage on text search 【发布时间】:2014-10-22 17:03:12 【问题描述】:

使用MongoDB v2.6,如果从大型结果集中对cursor 进行排序以得到overflow,这种情况并不少见。

cursor = db.collection.find(  "key" : "value"  )
cursor.sort(  "rank" : 1  )  // This can blow up

错误看起来很像:

运行器错误:溢出排序阶段缓冲数据使用量 33598393 字节超过内部限制 33554432 字节

在这种情况下,解决方案是将provide an index 作为排序条件,而不仅仅是键。

db.collection.ensureIndex(  "rank" : 1  )  // ascending

这很好用。


我在另一个地方遇到了这个问题,text index。按照text index creation 上的 MongoDB 手册中的说明,我已经做到了:

db.collection.ensureIndex(
    "$**": "text" ,
    name: "TextIndex" 
)

而且,这已经在集合中我的所有 ExtendedJSON 对象的所有字段中创建了一个文本索引。

搜索完美。

cursor = db.collection.find(  "$text" :  "$search" : "NEEDLE"   )
cursor.count()                      // w00t!  records that have NEEDLE in them

但是,尝试执行与以前相同的排序失败,即使排序字段存在索引:

db.collection.ensureIndex(  "rank" : 1  )  
cursor = db.collection.find(  "$text" :  "$search" : "NEEDLE"   )
cursor.sort(  "rank" : 1  )      // This blows up with the same error message

运行器错误:溢出排序阶段缓冲数据使用量 33598393 字节超过内部限制 33554432 字节

这是奇怪的部分。

在光标上迭代 执行排序工作正常,这就是我上面的计数。我什至可以通过光标单步查看 无序 结果,因此文本搜索显然有效。

但是,省略文本搜索会导致排序正常工作;这让我觉得它不是基于数量的,虽然我知道它实际上只是使用排序键的索引。

db.collection.ensureIndex(  "rank" : 1  )  
cursor = db.collection.find( )     // Get absolutely everything
cursor.sort(  "rank" : 1  )      // Well, sort now works again... hmm....

正如我必须通过提供一个索引来“帮助”Mongo,这样它就可以在不将所有记录放入内存的情况下进行排序,如何为文本索引实现这一点?

不幸的是,我无法获得explain plan,因为它也会产生相同的错误。如果我对.find() sans .sort() 的结果执行此操作,它会显示明显的结果——对数据进行全面扫描,没有IndexBounds 字段。


附录:我尝试对文本索引的不是一个字段,而是所有字段——因此是"$**"。作为实验,我在所有字段上手动执行了.ensureIndex(...),希望这对排序有所帮助。但请记住,我并不是要对文本字段进行排序——只是将其作为一种机制来获取与搜索条件匹配的 JSON 对象集合。一旦我拥有了该集合,并且确实获得了该集合,我将尝试按 rank 字段对其进行排序,该字段已经有一个索引并且可以在其他场景中使用。

【问题讨论】:

在上面的示例中,我并没有尝试获取文本搜索的排名,而只是使用文本搜索来选择记录(获取子集);记录本身具有与之关联的独立排名字段,因此目标是按该值对返回的子集进行排序——因此是上面的代码。 虽然两者都是索引,但文本索引和“普通”索引在它们的实际功能(以及底层数据结构)方面完全不同。文本索引无法像“普通”索引那样帮助您对文本字段的值进行排序。在文本字段上创建一个“正常”索引并使用它进行排序。但是,在您使用文本搜索的相同字段上进行排序似乎有点时髦,除非它们只是标题或其他东西。 @wdberkeley:(请参阅上面的附录。)它不是单个文本字段[在“文本字段”上方],而是所有字段“$**”[如文档所述]。虽然全文搜索正在工作,但它是返回的集合,我无法通过我预先存在的具有索引的“排名”字段进行排序,尽管我可以通过它对其他查询进行排序就好了。我希望我只是误解了你的建议,所以我有一条路可以尝试。你能描述一下'在文本字段上创建一个“正常”索引并使用它来排序'的意思吗?我有多个要在其中搜索的字段和一个要排序的字段。非常感谢。 好的。我明白发生了什么。文本索引阻止您使用排名索引进行排序。然后 MongoDB 尝试按等级对内存进行排序,但这样做会达到内存限制。我认为除了自己对其进行排序或将结果数限制为可排序数之外,没有其他方法可以解决此问题。如果我想到其他任何事情,我会告诉你的。 【参考方案1】:

也许这个link 会帮助你。

总结一下:你不应该在你的程序中间调用 ensureIndex,而是让 Mongoose 为你调用它。只需将标志index: true在您的架构中添加到您要用于排序的字段中。在您的情况下,将其添加到排名和命名应该没问题。至少这在我的项目中对我有用。

例子:

var schema = mongoose.Schema(
   ...
   normalText : String,
   rank :  type: Number, index: true,
   name :  type: String, index: true 
);

【讨论】:

感谢您的提示,斯特凡!作为一个仅供参考,只是为了让您放心,上面的示例是针对 mongo> shell 的,用于说明问题。我同意,不要将 ensureIndex 放在程序中。【参考方案2】:

希望这会有所帮助。 尝试添加这样的代码。 这是为了排序。 $cursor = $cursor.sort( "rank" : 1 )); 但是你需要对 $text 进行排序, 试试这个, $cursor = $cursor->sort( "text" : 1 );

谢谢。

【讨论】:

以上是关于MongoDB分页获取数据排序阶段缓存溢出问题的主要内容,如果未能解决你的问题,请参考以下文章

php 分页查询怎么redis缓存

基于redis做缓存分页

分页 3 - 仅在没有互联网时使用缓存数据,否则执行 API 请求

springboot整合mongodb复杂查询和分页查询

测试人员需要了解的缓存知识

初学redis分页缓存方法实现