首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >与本地pymongo使用相比,Mongoengine在大型文档上的使用非常慢。

与本地pymongo使用相比,Mongoengine在大型文档上的使用非常慢。
EN

Stack Overflow用户
提问于 2016-02-07 18:24:58
回答 1查看 5.6K关注 0票数 13

我有以下mongoengine模型:

代码语言:javascript
复制
class MyModel(Document):
    date        = DateTimeField(required = True)
    data_dict_1 = DictField(required = False)
    data_dict_2 = DictField(required = True)

在某些情况下,DB中的文档可能非常大(大约5-10MB),并且data_dict字段包含复杂的嵌套文档(dict列表,等等)。

我遇到了两个(可能相关的)问题:

  1. 当我运行原生pymongo find_one()查询时,它会在一秒钟内返回。当我运行MyModel.objects.first()时,需要5-10秒。
  2. 当我从DB查询单个大型文档,然后访问它的字段时,只需10-20秒就可以完成以下操作: M= MyModel.objects.first() val = m.data_dict_1.get(some_key)

对象中的数据不包含对任何其他对象的引用,因此它不是对象取消引用的问题。

我怀疑这与mongoengine内部数据表示的效率低下有关,这影响了文档对象的构造以及字段的访问。我能做些什么来改善这一点吗?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2016-02-08 16:52:08

TL;DR: mongoengine正在花费很长时间将所有返回的数组转换为dicts

为了测试这一点,我用一个带有DictField的文档和一个大型嵌套dict构建了一个集合。文档大约在5-10 in的范围内。

然后,我们可以使用timeit.timeit来确认使用pymongo和mongoengine读取的差别。

然后,我们可以使用愈伤图GraphViz来查看mongoengine到底花了这么长时间。

以下是完整的代码:

代码语言:javascript
复制
import datetime
import itertools
import random
import sys
import timeit
from collections import defaultdict

import mongoengine as db
from pycallgraph.output.graphviz import GraphvizOutput
from pycallgraph.pycallgraph import PyCallGraph

db.connect("test-dicts")


class MyModel(db.Document):
    date = db.DateTimeField(required=True, default=datetime.date.today)
    data_dict_1 = db.DictField(required=False)


MyModel.drop_collection()

data_1 = ['foo', 'bar']
data_2 = ['spam', 'eggs', 'ham']
data_3 = ["subf{}".format(f) for f in range(5)]

m = MyModel()
tree = lambda: defaultdict(tree)  # http://stackoverflow.com/a/19189366/3271558
data = tree()
for _d1, _d2, _d3 in itertools.product(data_1, data_2, data_3):
    data[_d1][_d2][_d3] = list(random.sample(range(50000), 20000))
m.data_dict_1 = data
m.save()


def pymongo_doc():
    return db.connection.get_connection()["test-dicts"]['my_model'].find_one()


def mongoengine_doc():
    return MyModel.objects.first()


if __name__ == '__main__':
    print("pymongo took {:2.2f}s".format(timeit.timeit(pymongo_doc, number=10)))
    print("mongoengine took", timeit.timeit(mongoengine_doc, number=10))
    with PyCallGraph(output=GraphvizOutput()):
        mongoengine_doc()

输出结果表明,与pymongo相比,mongoengine的速度非常慢:

代码语言:javascript
复制
pymongo took 0.87s
mongoengine took 25.81118331072267

生成的调用图非常清楚地说明了瓶颈所在:

从本质上说,mongoengine将对从db返回的每个to_python方法调用该方法。to_python相当慢,在我们的例子中,它被称为疯狂的次数。

Mongoengine用于优雅地将文档结构映射到python对象。如果您有非常大的非结构化文档( mongodb非常适合这种文档),那么mongoengine并不是真正合适的工具,您应该只使用pymongo。

但是,如果您知道这个结构,就可以使用EmbeddedDocument字段从mongoengine获得更好的性能。我运行了一个类似但不等效的测试本要点中的代码,输出如下:

代码语言:javascript
复制
pymongo with dict took 0.12s
pymongo with embed took 0.12s
mongoengine with dict took 4.3059175412661075
mongoengine with embed took 1.1639373211854682

所以你可以让mongoengine更快,但是pymongo更快。

更新

这里的pymongo接口的一个很好的快捷方式是使用聚合框架:

代码语言:javascript
复制
def mongoengine_agg_doc():
    return list(MyModel.objects.aggregate({"$limit":1}))[0]
票数 38
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/35257305

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档