sql >> Databasteknik >  >> NoSQL >> MongoDB

Mongoengine är mycket långsam på stora dokument jämfört med inhemsk pymongoanvändning

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]



  1. Dynamiska attribut med Rails och Mongoid

  2. Det gick inte att starta MongoDB 3.0.2-tjänsten på CentOS 7

  3. Zookeeper vs In-memory-data-grid vs Redis

  4. Delvis uppdatering av ett underdokument med nodejs/mongoose