廃止されたAPIを使用してMongoDBとの連携をどのように最適化したか、またはその仕様が何について言及していないか

画像



問題が発生すると、Javaのバックエンドとnode.jsのフロントエンドの間で、mongoDbがキャッシュ/バッファとして使用されました。 ビジネスの需要がmongoDbを介して短時間で大量を転送するように見えるまで(数分以内で最大20万件のレコード)、すべてが正常でした。 それほど重要ではないものについては、そのようなタスクが現れていることが重要です。 そして、ここで私はモンガの内部を理解しなければなりませんでした...





ラウンド0:

Write Concern = Acknowledgedでmonguに書き込みます。 額の中で最も一般的で簡単な方法。 この場合、mongoはすべてがエラーなしで記録され、一般的にはすべてが正常であることを保証します。 すべてが完全に書かれていますが、... 20万で20分以上死にます。 ふさわしくない。 額へのパスを消します。



画像

ラウンド1:

同じWrite Concern = Acknowledgedで一括書き込み操作を試みます。 良くなりましたが、それほどではありません。 彼は10〜15分で書き込みます。 奇妙な、実際には、より多くの加速が期待されていました。 では、先に進みましょう。



画像

ラウンド2:

Write ConcernをUnacknowledgedに変更し、ヒープへの一括書き込み操作を使用しようとしています。 一般的に、これは最善の解決策ではありません。もしもモンで何かがうまく行かなければ、私たちはそれを知ることができません。なぜなら、彼女はデータを彼女に報告するだけだからです。 一方、ビジネス要件によると、データは銀行取引ではなく、単一の損失はそれほど重大ではありません。また、ongのすべてが悪い場合は、すでに監視から学習します。 やってみます。 一方では、わずか1分で書かれていて、それは良いです(一括書き込み操作なしで、1分半も良いです)、他方では、問題が発生しました:書き込みの直後に、javaがnode.jsにゴーサインを与え、読み取りを開始すると、データ全体が入って、まったく入らない半分が読み込まれ、半分は読み込まれません。 非同期性のせいです-このWrite Concernでは、mongaはまだ書き込みを行っており、node.jsはすでに読み取りを行っているため、クライアントは、記録が終了することが保証される前に読み取る時間を持っています。 残念だ。





ラウンド3:

私たちは考え始めました。Thread.sleep(60秒)を書く、またはコントロールオブジェクトをmonguに書くというアイデアは、すべてのデータがロードされたことを示し、非常に曲がっているように見えます。 理論上、書き込み懸念はバルク書き込み操作中に最後の記録を遅くする必要があり、まったくしないため、バルク書き込み操作の速度がこれほど遅くなる理由を確認することにしました。 最後の部分の録音を待つのに非常に時間がかかるのは、なんとなく非論理的です。 Javaのmongaドライバーコードを見ると、特定のパラメーターmaxBatchWriteSizeによって制限されている一括操作のパッケージがあります。 デバッグでは、このパラメーターは500のみであることが示されています。つまり、実際には、500レコードのみのリクエストによってバルク全体がカットされるため、そのような結果は毎回確認され、これらの500レコードの完全なレコードを待ってから新しいリクエストを送信するなど、4,000回最大音量、そしてそれは非常に遅くなります。





ラウンド4

このmaxBatchWriteSizeパラメーターの由来を理解しようとしていますが、mongaドライバーがgetMaxWriteBatchSize()をmongaサーバーに要求していることがわかります。 モンガの設定でこのパラメーターを増やし、この制限をバイパスするというアイデアがありました。 仕様でこのパラメーターまたはクエリを見つけようとすると、結果はゼロになります。 さて、インターネットを見て、C ++でソースコードを見つけてください。 このパラメーターは、ソースコードにしっかりと接続された平凡な定数です。つまり、それを増やすことはできません。 行き止まり。





ラウンド5

インターネット上でより多くのオプションを探しています。 彼らは、100の並列スレッドを介してアップロードするオプションを試さないことにしました。DDosが独自のサーバーにmongaを持つことは一般的です(monga自体が着信要求を並列処理できるためです)。 次に、 getLastErrorなどのコマンドを見つけました。その本質は、すべての操作がデータベースに保存されるまで待機し、エラーコードまたは正常終了を返すことです。 強化された仕様は、このメソッドが古く、使用する必要がないことを納得させようとしています。mongaドライバーでは、このメソッドはdepricatedとマークされています。 ただし、 Write Concern = UnacknowledgedおよびBulk write with order modeでリクエストを送信し、 getLastError()を呼び出します。1分半の間、すべてのレコードを同期的に書き込み、getLastError()が待機しているため、クライアントはすべてのオブジェクトの完全なレコードの後に​​読み取りを開始します最後のレコードの終わり。パケットは互いに抑制しません。 さらに、エラーが発生した場合、getLastError()で確認します。 つまり、Acknowledgedで正確に高速バルク書き込みを取得しましたが、最後のパケットのみを待機します(ほとんどの場合、エラー処理はおそらく現在のAcknowledgedモードよりも悪化します。このコマンドは、おそらく最初のパケットでのみ発生したエラーを表示しません。最初のパケットは失敗し、最後のパケットは成功します-それほど素晴らしいものではありません)。



画像

それでは、Mongiの仕様は何について沈黙していますか?

1. 一括書き込み操作はあまりバルクではなく、パッケージ内の500〜1000リクエストの上限によって厳しく制限されます。 更新 :実際、私がちょうど発見したように、結局、1000の操作の上限について言及がありましたが、分析が実行されたバージョン2.4では1年以上前に魔法の定数についての言及はありませんでした。



2. 残念ながら、 getLastErrorのメカニズムは多少成功しており、新しいWrite Concernメカニズムはまだ完全に置き換えられていないか、古いコマンドを使用して作業をスピードアップできるため、論理的な動作は「大規模なバルクリクエストから最後のパケットのみが正常に記録されるのを待つ」 「mongでは実装されなかったため、



3. Write Concern = Unacknowledgedの問題は、データが失われてエラーが返されないことではなく、データが非同期に書き込まれ、クライアントがすぐにデータにアクセスしようとすると、データを受信しないか一部のみを受信するという事実に容易につながる可能性があること(書き込み直後に読み取りコマンドを指定する場合は重要です)。



4. mongaでは、このような限られたバルクによってクエリパフォーマンスが大幅に低下します。また、Acknowledged Write Concernが正しく実装されていないため、最後のパケットの記録が終了するまで待つのが適切です。



PS一般に、仕様にすべての情報がない場合、非標準の方法を使用して興味深い最適化の経験が得られました。



PPSまた、私のオープンソースプロジェクト[useful-java-links](https://github.com/Vedenin/useful-java-links/tree/master/link-rus)を参照することをお勧めします-おそらく最も有用なJavaライブラリのコレクション、フレームワークとロシア語の教育ビデオ。 このプロジェクトの同様の[英語版](https://github.com/Vedenin/useful-java-links/)もあり、オープンソースサブプロジェクト[Hello world](https://github.com/Vedenin/useful-javaを開始します。 -links / tree / master / helloworlds)を使用して、1つのMavenプロジェクト内のさまざまなJavaライブラリの簡単なサンプルのコレクションを準備します(サポートに感謝します)。



All Articles