TL;DR:mongoengine spenderar evigheter på att konvertera alla returnerade arrays till dicts
För att testa detta byggde jag en samling med ett dokument med ett DictField
med en stor kapslad dict
. Dokumentet är ungefär i ditt intervall på 5-10 MB.
Vi kan sedan använda timeit.timeit
för att bekräfta skillnaden i läsningar med pymongo och mongoengine.
Vi kan sedan använda pycallgraph och GraphViz att se vad som tar mongoengine så jäkla lång tid.
Här är koden i sin helhet:
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()
Och resultatet bevisar att mongoengine är väldigt långsam jämfört med pymongo:
pymongo took 0.87s
mongoengine took 25.81118331072267
Den resulterande samtalsgrafen illustrerar ganska tydligt var flaskhalsen är:
I huvudsak kommer mongoengine att anropa to_python-metoden på varje DictField
att den kommer tillbaka från db. to_python
är ganska långsam och i vårt exempel kallas det galet många gånger.
Mongoengine används för att elegant mappa din dokumentstruktur till pythonobjekt. Om du har väldigt stora ostrukturerade dokument (vilket mongodb är bra för) så är mongoengine inte riktigt rätt verktyg och du bör bara använda pymongo.
Men om du känner till strukturen kan du använda EmbeddedDocument
fält för att få något bättre prestanda från mongoengine. Jag har kört en liknande men inte likvärdig test kod i denna sammanfattning
och utgången är:
pymongo with dict took 0.12s
pymongo with embed took 0.12s
mongoengine with dict took 4.3059175412661075
mongoengine with embed took 1.1639373211854682
Så du kan göra mongoengine snabbare men pymongo är fortfarande mycket snabbare.
UPPDATERA
En bra genväg till pymongo-gränssnittet här är att använda aggregeringsramverket:
def mongoengine_agg_doc():
return list(MyModel.objects.aggregate({"$limit":1}))[0]