元ドキュメント: Bulk Load インポートモードのチューニングガイド
Bulk Load インポートモードのチューニングガイド
Bulk Load インポートモードで最適なパフォーマンスを達成し、システムリソースを最大限に活用するために、実践の中で蓄積したチューニングの知見を以下にまとめます。
順序付きインポート
順序付きインポートとは、データを主キーの昇順でインポートすることを指します。データベースは順序付きデータをより効率的に処理できます。順序なしのインポートと比較して、順序付きインポートはパフォーマンスを 20%〜300% 向上させます。
ここでの「順序」は2つのレベルに分かれます。
Bulk Load トランザクション内部の順序
Bulk Load トランザクション内部では、データが主キーの順序で並んでいる必要があります。
Bulk Load トランザクション間の順序
あるトランザクションが書き込むデータの最小主キーと最大主キーをそのトランザクションの範囲とした場合、トランザクションの範囲が互いに重複していなければ、トランザクション間は順序付けが可能とみなされます。つまり、2つのトランザクションが書き込むデータを主キーの昇順でソートした際に交差が存在しない場合、これらのトランザクションは順序付け可能です。
上記の2つの順序条件が同時に満たされている場合、データインポートは完全に順序付けされているとみなされ、最適なパフォーマンスが得られます。
条件1が満たされない場合
トランザクション内部の個々のレコードが主キー順にソートされていない場合、インポート前にパラメータ tdsql_bulk_load_allow_unsorted を有効にする必要があります。有効にしない場合はエラーで失敗します。tdsql_bulk_load_allow_unsorted を有効にすると、各トランザクションのデータはまず一時ファイルに格納されます。コミットフェーズで外部マージソートが実行され、最終的にソートされた状態で外部 SST ファイルに出力されます。トランザクション内部のデータが既に順序付きの場合、このステップはスキップされ、データは直接外部 SST ファイルに書き込まれます。そのため、トランザクション内部が順序なしの場合は、順序付きの場合よりも多くの CPU および I/O リソースを消費します。
条件2が満たされない場合
トランザクション間で生成される外部 SST ファイルの範囲が重複します。LSM-tree に挿入する際、重複により下位レベルに適切な挿入位置が見つからず、より多くの SST ファイルが最上位レベル(level-0)に挿入されることになります。これにより下位レベルへのコンパクション操作がトリガーされ、Bulk Load データインポート中に I/O リソースの一部が消費され、全体的なパフォーマンスに影響を与えます。
並列インポート
ディスクパフォーマンスが良好な場合(NVMe ディスク)、Bulk Load のパフォーマンスボトルネックは I/O ではなく CPU にある可能性が高いです。インポートスレッドの並列数(50〜100)を適切に増やすことで、インポート効率を向上させることができます。
システムは以下のパラメータ(スーパー管理者権限が必要)を使用して、Bulk Load インポートプロセスで生成される外部 SST ファイルおよびソート待ち一時データファイルの圧縮アルゴリズムを調整し、CPU、I/O、ディスク容量のバランスをとることができます:
tdstore_bulk_load_sorted_data_sst_compressiontdstore_bulk_load_unsorted_data_sst_compressiontdstore_bulk_load_temp_file_compression
SET GLOBAL tdstore_bulk_load_sorted_data_sst_compression=zstd,snappy,lz4,auto,nocompression,...;トランザクションサイズの制御
通常のトランザクションが未コミットデータをメモリ上の write batch に一時格納するのとは異なり、Bulk Load トランザクションは未コミットデータを外部 SST ファイルに直接書き込みます。このため、大規模トランザクションであってもメモリを過剰に消費して OOM(Out of Memory)を引き起こすことはありません。
一方で、Bulk Load トランザクションが小さすぎるのも適切ではありません。小さいトランザクションは小さな SST ファイルを生成するだけでなく、頻繁なトランザクションコミットによりインポートパフォーマンスも低下します。
通常、Bulk Load トランザクションのデータ量が 200MB 以上であれば、良好なパフォーマンスが得られます。なお、外部 SST ファイルではデフォルトで圧縮アルゴリズムが有効になっています。ZSTD を例にとると、元データとの圧縮率は10倍以上になる可能性があるため、1つの Bulk Load トランザクションで書き込む元データ量を 2GB 以上にすることを検討してください。
トランザクションの自動バッチコミット
パフォーマンス要件が高い大規模データ移行インポートで、移行フェーズ中に読み取りリクエストがない場合、Bulk Load トランザクションの自動バッチコミットを有効にすることも検討できます:
SET GLOBAL tdsql_bulk_load_allow_auto_organize_txn = ON;データベースが自動的に Bulk Load トランザクションデータを適切なサイズに蓄積してから自動コミットを実行することで、高いインポートパフォーマンスを維持します。
ただし、このオプションを有効にすると、クライアントにトランザクションコミット成功が返された時点で、対応するデータがまだバッチ蓄積中で自動コミットの閾値に達していない場合があり、データが一時的に参照できない可能性があります。インポート完了後に行数の検証を実施することを推奨します。
セカンダリインデックス
テーブルにセカンダリインデックスが存在する場合、セカンダリインデックスの数が多いほど Bulk Load のパフォーマンスは低下します。これは、セカンダリインデックスのデータが本質的に順序なしであり、追加のソート操作が必要となるため、インポート処理における計算オーバーヘッドが増加するためです。セカンダリインデックスが存在するシナリオでは、Bulk Load トランザクションのサイズを適切に大きくすることを推奨します。
バージョン 21.x 以降では、より推奨されるアプローチがあります:Bulk Load でデータをインポートする際、まずセカンダリインデックスなしでテーブルを作成します。インポート完了後にセカンダリインデックスを作成し、その際に Fast Online DDL 最適化を有効化(手動で有効化が必要)することで、全体的なパフォーマンスが向上します。
パーティションテーブル
コンピューティング層から見ると、各パーティションテーブルにはユニークな t_index_id が割り当てられます。実際の処理ロジックは通常のテーブルの処理と類似しています。1つの Bulk Load トランザクションが複数のパーティションテーブルにまたがるシナリオを考慮する必要があります。
Range パーティション + 順序付きインポートの場合
1つの Bulk Load トランザクション内のデータは通常1つの Range パーティション、または少数の隣接する Range パーティションに集中します。この場合、1つの Bulk Load トランザクションが多くのパーティションに関与することは通常なく、通常のテーブルと同様のシナリオとなるため、追加の対応は不要です。
Hash パーティション + 順序付きインポートの場合
1つの Bulk Load トランザクション内のデータは各 Hash パーティションに均等に分散されます。この場合、1つの Bulk Load トランザクションはほぼすべてのパーティションに関与します。このため、Bulk Load トランザクションのサイズをさらに大きくし、各 Hash パーティションに割り当てられるデータ量が少なくなりすぎないようにする必要があります。Hash パーティション数が N の場合、Bulk Load トランザクションサイズを通常のテーブルのシナリオの N 倍に拡大することを推奨します。現在、16個の Hash パーティションに分割したシナリオでは、パーティションなし(通常のテーブル)と比較してパフォーマンスが約 15% 低下します。
サブパーティションが存在する場合
1つの Bulk Load トランザクション内のデータがいくつのサブパーティションに関与するかを考慮し、各サブパーティションに割り当てられるデータ量が少なくなりすぎないようにする必要があります。
まとめ
1つの Bulk Load トランザクションが関与するパーティションの数が増えるほど、パフォーマンスは徐々に低下します。ストレージ層から見ると、これは1つの Bulk Load トランザクションがトランザクション参加者として過多の RG(Replication Group)を巻き込むためです。通常のトランザクションでも同様の問題があり、関与するパーティションが多いほど RG レベルの参加者が増え、パフォーマンスが低下します。