MongoDB中具有数千个查询的可伸缩性

时间:2014-06-10 21:39:51

标签: python performance mongodb scalability pymongo

我有一个没有错误运行的代码,但由于MongoDB中的查询数量很多,它实在很慢。

我有一个很大的python列表(大约5000个项目),我想在MongoDB中的文档中同时存在两个项目时进行测试。

mylist = ['apple','banana','melon','orange']
for i in mylist:
   for j in mylist[mylist.index(i):]:
       test = db.collection.find_one({'mylabel':{'$all':[i,j]}})
       if test:
          then do my stuff

其中mylabel也是包含mylist项目的列表。

在150-200件物品的清单中,完成后只需几分钟。但在像我这样的清单中,即使有更多的物品,我也需要几个小时。有没有办法在时间的基础上改进它?

根据要求编辑:

我还没有创建索引。这是我的数据库中的结构示例(但它太简单了)

{
 "_id": 1,
 "postid": 1,
 "mylabel":['apple','banana','melon']
}
{
 "_id": 2,
 "postid": 2,
 "mylabel":['banana','melon']
}

_id是MongoDB的标准ID,postid是我为文档提供的自定义ID,mylabel是标签列表。

2 个答案:

答案 0 :(得分:1)

所以在得到所有必需的信息之后,我的回答是:

  1. 您需要字段myLabel上的索引。这是你如何做到的:db.collection.ensureIndex({'myLabel': 1});我希望这会加快查询速度。这是一种标准技术。
  2. 除此之外,您可以尝试将整个集合带入内存中。或者如果你的内存不够,你可以升级它以提高性能。
  3. 如果您的收藏非常大,可以将其收藏。这将在您的查询中包含一些并行性。
  4. 就这段代码的复杂性而言,你不能做得比O(n ^ 2)好。

答案 1 :(得分:0)

对于数组中的5000个项目可能不是最好的事情(你可以试试)但是如果你有一个MongoDB 2.6或更高版本的聚合框架中的一些运算符,你至少可以加快速度。

为了简单起见只是用JavaScript注释,无论如何都没有与python代码有太大的不同

var mylist = ['apple','banana','melon','orange'];
db.collection.aggregate([

    // Match possible documents excluding single element and empty
    // arrays that cannot possibly match
    { "$match": {
        "$and": [
            { "mylabel": { "$in": mylist } },
            { "mylabel": { "$not": { "$size": 1 } } }
        ]
    }},

    // Project the size of the set-intersection
    { "$project": {
        "postid": 1,
        "mylabel": 1,
        "size": { "$size": { "$setIntersection": [ "$mylabel", "mylist" ] } }
    }},

    // Match only the documents with "size" greater than or equal to 2
    { "$match": { "size": { "$gte": 2 } }
])

非常自我解释,匹配包含至少一个匹配列表项的文档,并且必须具有自己的数组大小为2或更大才能产生可​​能的匹配。然后$setIntersection运算符进入比较数组并找到相同的项。生成的数组使用$size进行测试,在此上下文中是一个聚合运算符,用于确定结果数组的大小。

最终$match过滤掉任何未报告"尺寸"匹配的2个或更多项目。所以这些是包含匹配列表中至少两个项目的文档。这应该真的过滤掉结果,实际上就是你要求的。

查看匹配列表中是否包含5000个元素,或者尝试拆分匹配列表。它不应该打破BSON的限制,虽然我不认为这种比较在性能方面会很好,但它仍然不应该花费数小时。

您当前流程中最大的时间问题是需要触发所有两个组合的所有查询。这里的要点是否定网络流量并让引擎完成工作。

同样,您的find_one方法存在一个缺陷,即任何配对组合无疑都会有多个匹配。这意味着您当前的流程可能会错过实际符合条件的文档。

另外,请为您的数组字段添加索引。虽然此处的$match语句不会自行过滤所有内容,但很可能至少可以过滤掉集合中的某些文档,索引将有助于避免扫描集合中的每个文档。 / p>

最后,结果可能仍然是大量文档,因此建议使用"光标"作为输出方法,而不是默认值,这是一个大的列表。有关语法和示例,请参阅aggregate上的pymongo文档。


如果没有在MongoDB 2.6或更高版本上,则mapReduce可能是最安全的选择:

var mylist = ['apple','banana','melon','orange'];
db.collection.mapReduce(
    function () {

        result = mylist.filter(function(n) {
            return this.mylist.indexOf(n) != -1
        });

        if ( result.length >= 2 )
            emit( this._id, this );

    },
    function(){},
    {
        "query": {
            "$and": [
                { "mylabel": { "$in": mylist } },
                { "mylabel": { "$not": { "$size": 1 } } }
            ]
        },
        "out": "outputcollection",
        "scope": { "mylist": mylist }
    }
)

同样的事情,找到与" set intersection"匹配的文档。大小大于2."范围"中的参数和"查询"将只是将序列化为请求的python列表。