2016-02-07 7 views
8

私は、次のmongoengineモデルがあります:Mongoengineは、ネイティブpymongoの使用にcomapred大きな文書では非常に遅いです

class MyModel(Document): 
    date  = DateTimeField(required = True) 
    data_dict_1 = DictField(required = False) 
    data_dict_2 = DictField(required = True) 

いくつかのケースではDB内の文書は、(5〜10メガバイトの周りに)非常に大きくなる可能性があり、 data_dictフィールドには、複雑なネストされたドキュメント(dictsのリストなど)が含まれます。

私は2つの(おそらく関連)の問題に遭遇している:私はネイティブpymongoのfind_one()クエリを実行すると、それは秒以内に返す

  1. を。 MyModel.objects.first()を実行すると、5〜10秒かかります。
  2. 私がDBから単一の大規模な文書を照会し、そのフィールドにアクセスすると、それだけで次の操作を実行するために10〜20秒かかる:

    m = MyModel.objects.first() 
    val = m.data_dict_1.get(some_key) 
    

オブジェクト内のデータがありません他のオブジェクトへの参照を含んでいるので、逆参照するオブジェクトの問題ではありません。
mongoengineの内部データ表現のいくつかの非効率性に関連していると思われます。これは、ドキュメントオブジェクトの構築とフィールドアクセスに影響します。これを改善するために私ができることはありますか?

+0

2つの異なるものがあるようです。ネイティブメソッドは1つのドキュメントをロードして停止し、mongoengineはすべてのドキュメントをロードして最初のドキュメントを返します。 'find_one()'から 'list(db.collection.find())[0]'に変更してメソッドと等しくするようにしてください。 – Valijon

+0

また、mongoengineクエリでlimit(1)を追加しようとしましたが、それは役に立ちませんでした。ほとんどの時間は、すべてのネストされたオブジェクトを持つmongoengine Documentオブジェクトの構築に費やされていると思われます。 –

+0

ドキュメントが読み込まれると 'skip'と' limit'が動作します: /docs.mongoengine.org/guide/querying.html#query-operators](http://docs.mongoengine.org/guide/querying.html#query-operators) – Valijon

答えて

20

TL; DRは:mongoengineはこれをテストするにはdicts

にすべて返された配列を変換年齢を費やしている私は、大規模なネストされたdictDictFieldと文書のコレクションを構築しました。ドキュメントはおよそ5-10MBの範囲にあります。

次に、timeit.timeitを使用して、pymongoとmongoengineを使用して読み込みの違いを確認できます。

次に、私はpycallgraphGraphVizを使用して、何が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 

結果、コールグラフはかなり明確にどこボトルネックを示しています次のとおりです。

pycallgraph.png for mongoengine read of large doc hot spot in pycallgraph

基本的にmongoengineはdbから戻ってくるすべてのDictFieldに対してto_pythonメソッドを呼び出します。 to_pythonはかなり遅く、私たちの例ではそれは非常識な回数と呼ばれています。

Mongoengineは、ドキュメント構造をPythonオブジェクトにエレガントにマッピングするために使用されます。非常に大きな非構造化文書(mongodbは素晴らしい)があれば、mongoengineは本当に適切なツールではなく、pymongoを使用するだけです。

しかし、あなたが構造を知っていれば、EmbeddedDocumentフィールドを使用してmongoengineから少し良いパフォーマンスを得ることができます。私は同等ではないが類似の試験code in this gistを実行したと出力は次のようになりますので、あなたがより速く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 

が、まだはるかに高速pymongoです。

UPDATE

ここpymongoインターフェイスへの良好なショートカットが集約フレームワークを使用することです:

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

優れた分析、ありがとうございました! 私自身 !)。 mongoengineが埋め込みデータをDictField形式で保持することが賢明だとは思っていません。少なくとも、それを避ける方法はあります。私はmongoengineのgithubに関する問題を開いて、それが助けてくれることを願っています:https://github.com/MongoEngine/mongoengine/issues/1230 –

+0

ありがとうございます、はい、あなたは一般的なpymongo参照としてフィールドを指定できるように思われます。 –

+0

あなたの答えはとても役に立ちます。それを共有してくれてありがとう。 – Rahman

関連する問題