MongoDB
 sql >> Datenbank >  >> NoSQL >> MongoDB

Mongoengine ist bei großen Dokumenten im Vergleich zur nativen Pymongo-Nutzung sehr langsam

TL;DR:mongoengine verbringt Ewigkeiten damit, alle zurückgegebenen Arrays in Diktate umzuwandeln

Um dies zu testen, habe ich eine Sammlung mit einem Dokument mit einem DictField erstellt mit einem großen verschachtelten dict . Das Dokument liegt ungefähr in Ihrem Bereich von 5-10 MB.

Wir können dann timeit.timeit verwenden um den Unterschied in den Lesevorgängen mit Pymongo und Mongoengine zu bestätigen.

Wir können dann pycallgraph verwenden und GraphViz um zu sehen, was mongoengine so verdammt lange dauert.

Hier ist der vollständige Code:

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()

Und die Ausgabe beweist, dass Mongoengine im Vergleich zu Pymongo sehr langsam ist:

pymongo took 0.87s
mongoengine took 25.81118331072267

Das resultierende Anrufdiagramm zeigt ziemlich deutlich, wo der Flaschenhals liegt:

Im Wesentlichen ruft mongoengine die to_python-Methode für jedes DictField auf dass es von der db zurückkommt. to_python ist ziemlich langsam und wird in unserem Beispiel wahnsinnig oft aufgerufen.

Mongoengine wird verwendet, um Ihre Dokumentstruktur elegant Python-Objekten zuzuordnen. Wenn Sie sehr große unstrukturierte Dokumente haben (wofür Mongodb großartig ist), dann ist Mongoengine nicht wirklich das richtige Tool und Sie sollten einfach Pymongo verwenden.

Wenn Sie jedoch die Struktur kennen, können Sie EmbeddedDocument verwenden Felder, um eine etwas bessere Leistung von Mongoengine zu erhalten. Ich habe einen ähnlichen, aber nicht gleichwertigen Test-Code in diesem Kern ausgeführt und die Ausgabe ist:

pymongo with dict took 0.12s
pymongo with embed took 0.12s
mongoengine with dict took 4.3059175412661075
mongoengine with embed took 1.1639373211854682

Sie können Mongoengine also schneller machen, aber Pymongo ist noch viel schneller.

AKTUALISIEREN

Eine gute Abkürzung zur Pymongo-Schnittstelle ist hier die Verwendung des Aggregations-Frameworks:

def mongoengine_agg_doc():
    return list(MyModel.objects.aggregate({"$limit":1}))[0]