MongoRepositoryで大量のデータを挿入する際に発生する問題#3


過去の話題を思い出すと...


従来,20万個のデータを挿入する際の問題は,loopingによるsaveアクセスデータベースのリストサイズがネットワークオーバーヘッドをもたらすことである.
この問題を解決するために、毎回データベースにアクセスするのではなく、saveAllメソッドで解決できるバッチ挿入でリストとして一度に保存したいと考えています.
詳細は整理ホットスポット#2の記事で確認してください.😊

今度の問題はまた続いた。


もう一度言います.
「MongoRepositoryで大量のデータを挿入したときに発生する問題」シリーズには、次のものがあります.
お客様の要求に応じて、挿入時に受け入れられるデータはますます大きくなり、最終的には300万個に達し、処理中にどのような問題が発生したのか、なぜ300万個しか見えないのかを教えてくれます.
この文章は200万件の処理で発生した問題に基づいて書かれています!

確認してください!


文章を読む前に!挿入されたデータサイズは330 Byteです.
私はMacBookPro 19-(6コア、16 GBメモリ)デスクトップで実行しています.👌

200万個以上挿入した場合に発生するOOM問題


挿入されたデータの数を200万個以上に増やすと問題が発生しました!
発生したエラーログは以下の通りですか?!

1行目で確認できますよね?ほほほ
メインスレッドでメモリ出力の問題が発生しました.
jvmのスタックスペースでは、メモリがオーバーフローしているようです.
どうして溢れ出すの?私たちはerrorlogをもう一度チェックしました.
ArrayListの大きさはずっと大きくなっていて、発生したOOMのようで、
JavaがArrayListに要素を追加しようとすると、容量が配列の長さに等しい場合、容量が調整されます.次に、既存のElementDataをサイズ変更された配列にコピーします.😊
このような原理によって、ArrayListは増加して複製され、最終的にはエラーが発生する可能性がある.
「みんないい!でもどうしたの?いつ?ArrayListに要素が追加されたの?」
エラーログの次の点へ

💁 一番下から流れを見てみましょう!

  • saveAllでBulk Write Operation
  • が正常に呼び出されたことを確認.
    接続は
  • DBに接続され、saveAndReceiveが行われています.
  • BsonWriter操作が進行中で、DBのDocument値はJavaのオブジェクトのようです.
  • は、生成されたオブジェクトのStreamをcollectによってArrayListにマージする.
  • のマージ中、ArrayListは増大、複製、増幅、複製を続けた.
  • 結論:


    ああ...saveAllを実行すると、データベースに正常に挿入されたオブジェクトがArrayListとして返されます.この過程で、動的メモリのサイズがjvmのheap sizeを超えています.

    どうしよう。


    MySQLとJpaRepositoryを使用する場合は、saveAllの後に返される値を拒否できます.
    しかし、MongoRepositoryはそれを拒否する方法が見つからない.これは私の不足かもしれない.

    結論は、


    データ・リスト全体をブロック単位で別々に送信します。


    では、戻り値も小さいのでOOMの問題は出ませんが、中間GCを適用すれば最終的には問題ないでしょうか…?


    このような結論に達し、すぐにテストを行いました!

    ブロックコード

    public void insertAll(MetaDataCreateAllRequestDto metaDataCreateAllRequestDto){
    
           ...
           List<MetaData> metaDataList = new ArrayList<>();
    
            for(Document body : bodyList){
                MetaData metaData = new MetaData().builder()
                        .projectId(projectId)
                        .body(body).build();
                metaDataList.add(metaData);
            }
    
            int chunk_size = 1000;
            for (List<MetaData> batch : Lists.partition(metaDataList,chunk_size)) {
                metaDataRepository.saveAll(batch);
            }
    
          ...
     }
    予想通り、上記コードを使用して挿入すると、200万個のOOMが発生しないことが確認されました!!

    🥇 整理する。


    データベースにデータを挿入し、正常に実行された値を返すと、OOMが生成されます.
    全く思いつかないところで起こったミスなので、慌てていましたが、これは面白い経験で、原因を一つ一つ見つけて、どのように解決するかを考えることができます.ほほほ👍😏😏

    次の文章の予告..。


    何の面白い経験だハハ...
    300万個入れたらすぐに爆発しちゃったハハハ
    次の記事では、300万を挿入したときに爆発する原因と、なぜ300万からそれ以上に増やせないのかを書きます^^
    読んでくれてありがとう!間違ったところがあったらメッセージを残してください.私たちは勉強を続けます.