ニーズの声明
Excelレポートは10w以上のデータをエクスポートします。
私たちのインポートおよびエクスポート取引システムでは、インポートされたデータが少ないため、効率をあまり追求しない可能性があります。しかし、二次開発バージョンでは、インポート中のExcel行数は10w以上であり、データベースに挿入されるデータの量は3nを超えると推定されます。つまり、Excelの10w行の場合、少なくとも30w行のデータがデータベースに挿入されます。
いくつかの詳細
データのインポート:インポートに使用されるテンプレートはシステムによって提供され、形式はxlsx(65535行以上のデータをサポート)です。ユーザーは、ヘッダーに従って対応する列に対応するデータを書き込みます。
データ検証:データ検証には2つのタイプがあります。2
。フィールド長、フィールド正規式検証など。メモリ検証では外部データの相互作用はありません。パフォーマンスへの影響が少ない、チケット番号がシステム内の既存のチケット番号と同じであるかどうかなどのデータの再現性の検証(データベースにクエリを実行する必要があり、パフォーマンスに大きく影響します)
1.データ挿入:テスト環境データベースはMySQL 5.5を使用し、データベースはテーブルに分割されず、接続プールはDruidを使用します
反復記録
初版:POI +行ごとのクエリと校正+行ごとの挿入
このバージョンは最も古いバージョンです。ネイティブPOIを使用して、Excelの行を手動でArrayListオブジェクトにマップし、それらをListに格納します。コードの実行手順は次のとおり
です。1。Excelを手動でListに読み込みます。
2.ループトラバーサル、ループ内で次の手順を実行します
。フィールドの長さを確認します
購入および販売契約の現在の請求書がシステムに存在するかどうかの検証など、一部のクエリデータベースの検証では、請求書テーブルをクエリする必要があります。
現在の行データを書き込む
3.エラー/チェックに失敗した場合は、実行結果を返します。プロンプト情報が返され、データがロールバックされることは明らかです。この実装は急いで実行する必要があります。その後の使用は少なくなり、パフォーマンスの問題は検出されない可能性がありますが、1桁/ 10桁のデータに最適です。次の明らかな問題があります。
データベースへのクエリのチェックサムでは、データの行ごとに1回データベースにクエリを実行する必要があります。アプリケーションがデータベースにアクセスするためのネットワークI / Oの数はn倍に拡大され、時間はn倍に拡大されます。
書き込まれたデータも行ごとに書き込まれ、問題は上記と同じです。
データの読み取りにはネイティブPOIが使用され、コードは非常に冗長であり、保守性が低くなります。
第2版:EasyPOI +キャッシュデータベースクエリ操作+バッチ挿入
初版で分析された3つの問題を考慮して、次の3つの方法を使用して最適化しました。
データをキャッシュし、時間とスペースを交換します。
データベースを行ごとに照会する時間コストは、主に前後のネットワークIOにあり、最適化方法も非常に単純です。検証に参加しているすべてのデータをHashMapにキャッシュします。HashMapに直接移動してヒットします。
例:チェックラインに運送状が存在する場合、契約番号は元々、契約IDと一致するように運送状テーブルを照会するために使用されていました。見つかった場合、検証は合格し、生成されたパッキングリストIDであり、検証が失敗した場合、エラーメッセージがに返されます。ユーザー。
有効期限が切れても契約は更新されません。したがって、SQLの一部を使用し、パッキングリストの下のすべての注文をキーとして使用し、売買契約IDを値としてHashMapに保存します。その後の検証では、HashMapのカスタムSessionMapperをヒットするだけで済みます。Mybatisは、次のクエリをネイティブにサポートしていません。結果はHashMapに直接書き込まれ、SQLクエリの結果セットを処理するためにMapResultHandlerを指定するようにSessionMapperをカスタマイズする必要があります。
@Repository
public class SessionMapper extends SqlSessionDaoSupport {
@Resource public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
super.setSqlSessionFactory(sqlSessionFactory); }
// 区域楼宇单元房号 - 房屋ID @SuppressWarnings("unchecked") public Map<String, Long> getHouseMapByAreaId(Long areaId) { MapResultHandler handler = new MapResultHandler();
this.getSqlSession().select(BaseUnitMapper.class.getName()+".getHouseMapByAreaId", areaId, handler); Map<String, Long> map = handler.getMappedResults(); return map; }
MapResultHandler 处理程序,将结果集放入 HashMap
public class MapResultHandler implements ResultHandler {
private final Map mappedResults = new HashMap(); @Override public void handleResult(ResultContext context) {
@SuppressWarnings("rawtypes") Map map = (Map)context.getResultObject(); mappedResults.put(map.get("key"), map.get("value")); }
public Map getMappedResults() {
return mappedResults; }}
値を使用してバッチ挿入する
MySQL挿入ステートメントは、値()、()、()を使用して、一度に複数行のデータを挿入することをサポートしています。バッチ挿入は、mybatisforeachとjavaコレクションを組み合わせて実現できます。コードは次のように記述されています。
<insert id="insertList"> insert into table(colom1, colom2) values <foreach collection="list" item="item" index="index" separator=","> ( #{item.colom1}, #{item.colom2}) </foreach></insert>
EasyPOIを使用したExcelの読み取りと書き込み
EasyPOIは、注釈ベースのインポートとエクスポートを使用します。注釈を変更することでExcelを変更できます。これは、非常に便利で保守が簡単です。
第3版:EasyExcel +キャッシュデータベースクエリ操作+バッチ挿入
2番目のバージョンでEasyPOIを採用した後は、数千または数万のExcelデータを簡単にインポートできますが、時間がかかります(5Wデータは約10分でデータベースに書き込まれます)が、その後のインポート操作は基本的にサイドで開発されます。ログを見ながらインポートすると、それ以上の最適化はありません
慌てる必要はありません。GITHUBにアクセスして、他のオープンソースプロジェクトを見つけてください。現時点ではAli EasyExcel
見えます:EasyExcelはEasyPOIと同じ注釈方法を使用してExcelの読み取りと書き込みを行うため、EasyPOIからの切り替えは非常に便利で、数分で完了します。
それは確かにAliGreat Godによって説明されているとおりです。41w行、25列、45.5mのデータ読み取りには平均50秒かかるため、EasyExcelを使用して大きなExcelを読み取ることをお勧めします。
第4版:データ挿入速度の最適化
第2版に挿入するときは、行ごとに挿入する代わりに、値のバッチ挿入を使用しました。長いSQLは30000行ごとにスプライスされ、順番に挿入されます。インポート方法全体は、最も時間がかかり、非常に手間がかかります。その後、スプライシングごとの行数を10000、5000、3000、1000、500に減らしたところ、最速の実行は1000であることがわかりました。
インターネット上のinnodb_buffer_pool_sizeの説明と組み合わせると、書き込み操作中にメモリのしきい値を超えたSQLが長すぎて、ディスクスワップが発生したことが原因だと思います。速度が制限されており、テストサーバーのデータベースパフォーマンスはあまり良くありません。彼はあまり多くの挿入を処理できません。したがって、最終的には、毎回1000個のインサートが使用されます。
1000回挿入するたびに、データベースのCPUを消耗させるために、ネットワークIOの待機時間を利用する必要があります。これを解決するには、複数のスレッドが必要です。最も単純なマルチスレッドは、並列ストリームを使用して実現できます、次に、並列ストリームを使用してコードをテストしました。10w行のexcel、42wバックオーダー、42wレコードの詳細、2wレコード、16スレッドを並列にデータベースに挿入、毎回1000行。挿入時間は72秒、合計インポート時間は95秒です。
パフォーマンスに影響を与えるその他のコンテンツ
ログ。forループに多くの情報ログを出力しないようにします。
最適化の過程で、特にパフォーマンスに影響を与えるものも見つかりました。情報ログは引き続き41w行、25列、45.5mのデータを使用し、開始から読み取りまでの間に1000行ごとに情報ログを出力し、キャッシュします。検証データ-検証後、3つ以上の情報ログが各行に出力されます。ログフレームワークはSlf4jを使用します。印刷してディスクに保存します。以下は、ログを印刷する場合とログを印刷しない場合の効率の違いです。
総括する
Excelのインポート速度を向上させる方法:
より高速なExcel読み取りフレームワークを使用します(Ali EasyExcelをお勧めします)
データベースと対話する必要がある検証では、ビジネスロジックに従ってキャッシュを適切に使用します。時間にスペースを
使用するvalues()、()、()を使用して、長いSQLを結合し、複数行のデータを一度に挿入します
複数のスレッドを使用してデータを挿入し、ネットワークIO待機時間を活用します(並列ストリームの使用を推奨、使いやすい)
無駄なログをループで印刷しないでください