Skip to content

元ドキュメント: Bulk Load 高速インポート

Bulk Load 高速インポート

概要

TDSQL Boundless は Bulk Load 方式によるデータベースへの高速データインポートをサポートしています。従来の SQL トランザクションベースの書き込み方式と比較して、Bulk Load 高速インポートモードはパフォーマンスが通常 5〜10 倍以上向上し、ビジネス展開フェーズでの既存の大規模データベースから新しい TDSQL Boundless クラスタへの移行に適しています。

現在、INSERT INTO / REPLACE INTO ステートメントによる Bulk Load データインポートをサポートしています。

動作原理

通常トランザクションの書き込みフロー

データはトランザクションのコミット前にトランザクションコンテキストの write batch に一時格納されます。コミットフェーズに入ると、write batch 内のデータはまずディスクに書き込まれてトランザクションログを形成し、Raft プロトコルを通じて各レプリカが存在するノードに同期されます。マジョリティが形成された後、データはまずメモリ内の memtable 構造に書き込まれ、memtable が満杯になるとフラッシュされてディスク上に SST データファイルが形成されます。SST ファイルは LSM-tree 構造で階層的に組織・格納され、バックグラウンドの非同期スレッドが compaction 操作を実行してデータのマージとクリーンアップを行います。

Bulk Load の書き込みフロー

Bulk Load インポートの書き込みフローは、通常トランザクションの冗長な書き込みパスをできるだけバイパスし、データを直接圧縮されたデータファイルに書き込みます。

具体的なフロー:

  1. Bulk Load トランザクションの書き込みリクエストを受信したノード(ソースノード)は、まず SQL ステートメントを解析してキーバリュー(KV)にエンコードします。

    • ソート済みデータ: 直接外部 SST ファイルに書き込みます。
    • 未ソートデータ: まず一時データファイルに書き込み、外部マージソートを実行して外部 SST ファイルを生成します。
  2. Bulk Load トランザクションがコミットフェーズに入ると、ソースノードは MC コントロールノードから最新の Replication Group(RG)ルーティング情報を取得し、外部 SST ファイルが最終的に属する RG とレプリカの場所を決定します。

  3. RG ルーティング情報に基づいて、ソースノードは外部 SST ファイルを RG レプリカが存在するノード(データノード)に送信します。

  4. 外部 SST ファイルがすべてのデータノードに受信され検証が成功したことを確認した後、ソースノードは RG Leader データノードに Bulk Load トランザクションのコミットリクエストを送信します。RG Leader データノードが Bulk Load commit log(Raft ログ)を同期し、ログがマジョリティを形成した後、このログの実行またはリプレイにより、RG レプリカが存在するデータノードが外部 SST ファイルを LSM-tree の適切な位置に直接挿入します。これでデータがデータベースに書き込まれます。

通常トランザクションと比較した主な最適化ポイント

  • トランザクション競合検出を実行しない
  • データをメモリの write batch に一時保存する必要がなく、直接 SST データファイルにディスク書き込み
  • データをトランザクションログに書き込んで同期する必要がない(注: Bulk Load トランザクションもプライマリ・セカンダリのデータ書き込み整合性を維持するための commit log を書き込みますが、トランザクションデータをログにシリアライズする必要はなく、必要な最小限のメタ情報のみを同期)
  • SST データファイルは flush、compaction 操作で LSM-tree の最上層から入る必要がなく、外部 SST ファイルを LSM-tree のできるだけ下位のレベルに直接挿入

これらの Bulk Load の対象を絞った最適化により、CPU、メモリ、I/O リソースの消費が大幅に削減され、インポートパフォーマンス向上の鍵となっています。

使用方法と制限事項

以下では、Bulk Load インポートに関連するパラメータ、使用制限、およびパフォーマンスに関するベストプラクティスを紹介します。

関連パラメータ

セッション変数

パラメータ名デフォルト値説明
tdsql_bulk_loadfalseBulk Load モードを有効化します。条件を満たす SQL ステートメントが Bulk Load 方式で直接データを書き込みます。
tdsql_bulk_load_allow_unsortedfalseBulk Load インポート時にプライマリキーデータの順不同をサポートするかどうか。有効にすると、インポートデータに対して追加のソートが行われるため、インポート速度が低下します。
tdsql_bulk_load_allow_sktrueセカンダリインデックスデータのインポートをサポートするかどうか。インポート対象のテーブルにセカンダリインデックスが既に存在する場合、この変数を true に設定する必要があります。そうしないと Bulk Load 最適化が適用されません(通常のトランザクションロジックまたは batch put 最適化にフォールバック)。有効時は、データソース側でデータの一意性を保証する必要があります(プライマリキーの競合がないこと)。ユニークセカンダリインデックスが存在する場合は、セカンダリインデックスデータの一意性も保証する必要があります。そうしないと、プライマリキーとセカンダリインデックスの不整合や、セカンダリインデックスデータの一意性制約の違反が発生する可能性があります。
tdsql_bulk_load_allow_insert_ignoretrueINSERT IGNORE 構文を含む SQL ステートメントで Bulk Load 最適化を許可するかどうか。注意: 有効時は、データソース側でデータの一意性を保証する必要があります。Bulk Load モードではプライマリキーの競合チェックを行わないため、IGNORE キーワードが含まれていても新しいデータが古いデータをサイレントに上書きし、IGNORE 構文の動作(新しいデータを破棄して古いデータを保持)に従いません。
tdsql_bulk_load_allow_auto_organize_txnfalseBulk Load トランザクションの自動バッチコミットを有効化するかどうか。データベースが Bulk Load トランザクションデータを適切なサイズに蓄積した後に自動コミットを開始します。注意: このオプションを有効にすると、クライアントにトランザクションコミット成功が返された時点でも、対応するデータがまだバッチ蓄積中で自動コミットのデータ量閾値に達していない場合があるため、データが表示されない場合があります。通常、パフォーマンス要件が高い大規模データ移行インポートで、移行フェーズ中に読み取りリクエストがなく、このフェーズのデータにリアルタイムの可視性要件がなく、インポート完了後に最終的な整合性状態に到達できればよいシナリオで使用されます。

使用制限

Bulk Load は TDSQL Boundless へのデータインポートの「銀の弾丸」(No Silver Bullet)ではありません。全体として、Bulk Load インポートモードはトランザクションの ACID 属性をある程度犠牲にして、より高いインポートパフォーマンスを得るものです。例えば、Bulk Load トランザクション間では競合検出を行わないため、ソースデータ内のデータ競合はユーザーが処理する必要があります。また、Bulk Load トランザクションのコミットが失敗した場合、複数の RG のデータが関係していると、部分コミット現象(一部の RG のデータ書き込みが成功し、一部が失敗)が発生する可能性があり、これはトランザクションの原子性に違反します。コミット失敗の場合、リトライ成功後にデータの最終的な整合性に到達します。

構文レベル

  • SET SESSION tdsql_bulk_load = ON で Bulk Load モードを有効化する必要があります。
  • INSERT INTO / REPLACE INTO ステートメントのみ最適化されます。他のステートメントは Bulk Load モードをサポートしません。
  • INSERT INTO / REPLACE INTO 形式の SQL は複数の VALUES を含む必要があります。1 行のみの場合、Bulk Load モードはサポートされません。
  • INSERT INTO SET / REPLACE INTO SET 形式の SQL は 1 行しか書き込めないため、Bulk Load モードをサポートしません。
  • INSERT INTO ... ON DUPLICATE KEY UPDATE は最適化されません。Bulk Load モードでは既存のプライマリキーを検出しないため、UPDATE 操作を実行できません。
  • INSERT INTO は実質的に REPLACE INTO の構文で実行されます。インポートする新しいデータと古いデータの間にプライマリキーの競合がある場合、エラーは報告されず、新しいデータが古いデータをサイレントに上書きします。
  • IGNORE 構文を含む SQL ステートメント(INSERT IGNORE INTO)の場合、事前に SET SESSION tdsql_bulk_load_allow_insert_ignore = ON を設定する必要があります。そうしないと Bulk Load インポートが実行できません。
  • INSERT IGNORE INTO の場合、Bulk Load インポート時にはソースデータ側でデータの一意性を保証する必要があります。そうしないと、Bulk Load モードではプライマリキーデータがサイレントに上書きされ 1 行のみ保持されるため、IGNORE 構文に違反します。

セカンダリインデックス

  • テーブルにセカンダリインデックスがある場合、事前に SET SESSION tdsql_bulk_load_allow_sk = ON を設定する必要があります。そうしないと Bulk Load 最適化が適用されません。
  • テーブルにセカンダリインデックスがある場合、Bulk Load モードではソースデータ側でデータの一意性を保証する必要があります。そうしないと、プライマリキーデータはサイレントに上書きされますが、セカンダリインデックスデータのエンコード後の key には一意性がないため、セカンダリインデックスデータの新旧 2 つのレコードが共に保存され、プライマリキーとセカンダリインデックスの不整合が発生します。
  • セカンダリインデックスを構築する必要があるテーブルの場合、21.x 以降のバージョンでは、Bulk Load でデータをインポートする際にまずセカンダリインデックスなしでテーブルを作成することを推奨します。インポート完了後にセカンダリインデックスを作成し、作成プロセスで fast online DDL 最適化を有効化(手動で有効化が必要)すると、パフォーマンスが向上し、「ソースデータの一意性」の制限を回避できます。
  • テーブルにユニークセカンダリインデックスがある場合、ソースデータ側でデータの一意性を保証する必要があります。プライマリキーの競合がなく、セカンダリインデックスデータの一意性にも違反しないことが必要です。

その他の制限

  • システムテーブルは Bulk Load モードをサポートしません。システムテーブルには大規模書き込みが発生すべきでなく、システムテーブルに問題が発生するとクラスタが起動できなくなるため、より堅牢な書き込みパスを使用します。
  • トリガーが存在するテーブルは Bulk Load モードでのインポートをサポートしません。
  • Bulk Load モードは現在 DDL と排他的です。テーブルで進行中の DDL がある場合、Bulk Load モードはサポートされません。Bulk Load モードでデータインポート中の場合、新しい DDL リクエストは拒否されます。
  • Bulk Load モードでインポートされたデータは binlog を生成せず、ディザスタリカバリクラスタのセカンダリインスタンスに同期されません。

パフォーマンスチューニング

Bulk Load インポートモードで理想的なパフォーマンスを達成し、システムリソースを最大限に活用するために、実践で蓄積したチューニングの知見を以下にまとめます。

順序付きインポート

順序付きインポートとは、プライマリキーの昇順でデータをインポートすることを指し、データベースがソート済みデータをより効率的に処理できます。順不同のインポートと比較して、順序付きインポートはパフォーマンスが 20%〜300% 向上します。

ここでの順序には、2 つのレベルがあります。

  • Bulk Load トランザクション内部の順序: 1 つの Bulk Load トランザクション内で、データがプライマリキー順に並んでいる必要があります。
  • Bulk Load トランザクション間の順序: 1 つのトランザクションが書き込むデータの最小プライマリキーと最大プライマリキーをそのトランザクションの区間とし、トランザクションの区間同士が重複していなければ、トランザクション間は順序付けられていると見なされます。

上記 2 つの条件が同時に満たされた場合、データインポートは完全に順序付けられているとみなされ、パフォーマンスが最適になります。

第 1 条件が満たされない場合(トランザクション内のレコードがプライマリキー順でない場合)、インポート前にパラメータ tdsql_bulk_load_allow_unsorted を有効化する必要があります。有効化すると、各トランザクション内のデータはまず一時ファイルに蓄積され、コミットフェーズで外部マージソートが実行された後、順序どおりに外部 SST ファイルに出力されます。トランザクション内のデータが順序付けられている場合、このステップは不要で直接外部 SST ファイルに書き込まれます。したがって、トランザクション内の順不同は順序付きよりも多くの CPU と I/O リソースを消費します。

第 2 条件が満たされない場合、トランザクション間で生成される外部 SST ファイルの区間が重複し、LSM-tree に挿入する際に下位レベルに適切な挿入位置がないため、より多くの SST ファイルが最上位レベル(level-0)に挿入され、下位レベルへの compaction 操作が発生し、Bulk Load データインポート中に I/O リソースの一部が消費され、全体的なパフォーマンスに影響を与えます。

並行インポート

ディスクパフォーマンスが良好な場合(NVMe ディスク)、Bulk Load のパフォーマンスボトルネックは I/O ではなく CPU にある可能性が高いです。インポートスレッドの並行数を適切に増やして(50〜100)インポート効率を向上させてみてください。

システムは tdstore_bulk_load_sorted_data_sst_compressiontdstore_bulk_load_unsorted_data_sst_compressiontdstore_bulk_load_temp_file_compression パラメータ(スーパー管理者権限が必要)を通じて、Bulk Load インポートプロセスで生成される外部 SST ファイルとソート待ちの一時データファイルが使用する圧縮アルゴリズムを調整し、CPU と I/O、ディスク容量のバランスをとることができます。

sql
SET GLOBAL tdstore_bulk_load_sorted_data_sst_compression=zstd,snappy,lz4,auto,nocompression,...;

トランザクションサイズの制御

通常のトランザクションがコミット前のデータをメモリの write batch に一時保存するのとは異なり、Bulk Load トランザクションはコミット前のデータを直接外部 SST ファイルに書き込みます。そのため、大きなトランザクションによるメモリ過剰消費(OOM)は発生しません。

逆に、Bulk Load トランザクションは小さすぎないようにする必要があります。小さすぎると、一方では小さな SST ファイルが生成され、もう一方では頻繁な Bulk Load トランザクションコミットによりインポートパフォーマンスも良くなりません。

通常、1 つの Bulk Load トランザクションのデータ量が 200 MB 以上であれば、良好なパフォーマンスが得られます。外部 SST ファイルはデフォルトで圧縮アルゴリズムが有効になっており、ZSTD の場合、元データと比較して圧縮率が 10 倍以上になる可能性があるため、1 つの Bulk Load トランザクションの元データ量を 2 GB 以上にすることを検討してください。

トランザクション自動バッチコミット

パフォーマンス要件がより高い大規模データ移行インポートで、移行フェーズ中に読み取りリクエストがないシナリオでは、Bulk Load トランザクションの自動バッチコミットの有効化も検討できます。

sql
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 パーティションに集中します。この場合、Bulk Load トランザクションは多くのパーティションに関与しないため、通常のテーブルのシナリオと類似し、追加の処理は不要です。

  • HASH パーティション + 順序付きインポート: 1 つの Bulk Load トランザクション内のデータは各 HASH パーティションに均等に分散されます。この場合、1 つの Bulk Load トランザクションはほぼすべてのパーティションに関与します。Bulk Load トランザクションのサイズをさらに増大させ、各 HASH パーティションに割り当てられるデータ量が少なくなりすぎないようにする必要があります。N 個の HASH パーティションがある場合、通常のテーブルシナリオの N 倍の Bulk Load トランザクションサイズを推奨します。現在、16 個の HASH パーティションのシナリオでは、パーティションなし(通常のテーブル)と比較してパフォーマンスが約 15% 低下します。

  • サブパーティションが存在する場合: 1 つの Bulk Load トランザクション内のデータがいくつのサブパーティションに関与するかを考慮し、各サブパーティションに割り当てられるデータ量が少なすぎないようにする必要があります。

**要約すると、**1 つの Bulk Load トランザクションが関与するパーティション数が多いほど、パフォーマンスは徐々に低下します。ストレージ層から見ると、これは 1 つの Bulk Load トランザクションがトランザクション参加者として過剰な RG に関与していることに相当します。通常のトランザクションにも同様の問題があり、関与するパーティションが多いほど、RG レベルの参加者が多くなり、パフォーマンスが低下します。

Tencent Cloud プロダクトドキュメント