MongoDB
 sql >> база данни >  >> NoSQL >> MongoDB

Mongoengine е много бавен при големи документи в сравнение с естественото използване на pymongo

TL;DR:mongoengine прекарва векове в преобразуване на всички върнати масиви в dicts

За да тествам това, създадох колекция с документ с DictField с голям вложен dict . Документът е приблизително във вашия диапазон от 5-10 MB.

След това можем да използваме timeit.timeit за потвърждаване на разликата в четенията с помощта на pymongo и mongoengine.

След това можем да използваме pycallgraph и 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()

И резултатът доказва, че mongoengine е много бавен в сравнение с pymongo:

pymongo took 0.87s
mongoengine took 25.81118331072267

Получената графика на повикванията илюстрира доста ясно къде е тясното място:

По същество mongoengine ще извика метода to_python на всяко DictField че се връща от db. 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]



  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. moveChunk не успя да включи TO-shard в прехвърлянето на данни:не може да приеме нови парчета, защото

  2. Актуализиране на полето със стойност на друго поле в документа

  3. Как мога да внедря разрешения на ниво поле за MongoDB?

  4. грешка при синтактичния анализ на mongoexport JSON

  5. Обединяване на документи с техните вложени масиви и техните вложени масиви