我有以下mongoengine模型:
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列表,等等)。
我遇到了两个(可能相关的)问题:
对象中的数据不包含对任何其他对象的引用,因此它不是对象取消引用的问题。
我怀疑这与mongoengine内部数据表示的效率低下有关,这影响了文档对象的构造以及字段的访问。我能做些什么来改善这一点吗?
发布于 2016-02-08 16:52:08
TL;DR: mongoengine正在花费很长时间将所有返回的数组转换为dicts。
为了测试这一点,我用一个带有DictField的文档和一个大型嵌套dict构建了一个集合。文档大约在5-10 in的范围内。
然后,我们可以使用timeit.timeit来确认使用pymongo和mongoengine读取的差别。
然后,我们可以使用愈伤图和GraphViz来查看mongoengine到底花了这么长时间。
以下是完整的代码:
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的速度非常慢:
pymongo took 0.87s
mongoengine took 25.81118331072267生成的调用图非常清楚地说明了瓶颈所在:


从本质上说,mongoengine将对从db返回的每个to_python方法调用该方法。to_python相当慢,在我们的例子中,它被称为疯狂的次数。
Mongoengine用于优雅地将文档结构映射到python对象。如果您有非常大的非结构化文档( mongodb非常适合这种文档),那么mongoengine并不是真正合适的工具,您应该只使用pymongo。
但是,如果您知道这个结构,就可以使用EmbeddedDocument字段从mongoengine获得更好的性能。我运行了一个类似但不等效的测试本要点中的代码,输出如下:
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接口的一个很好的快捷方式是使用聚合框架:
def mongoengine_agg_doc():
return list(MyModel.objects.aggregate({"$limit":1}))[0]https://stackoverflow.com/questions/35257305
复制相似问题