11

大規模な MySQL データベースで作業しており、特定のテーブルでの INSERT パフォーマンスを改善する必要があります。これには約 2 億行が含まれており、その構造は次のとおりです。

(少し前提:私はデータベースの専門家ではないので、私が書いたコードは間違った基盤に基づいている可能性があります。私の間違いを理解するのを手伝ってください:))

CREATE TABLE IF NOT EXISTS items (
    id INT NOT NULL AUTO_INCREMENT,
    name VARCHAR(200) NOT NULL,
    key VARCHAR(10) NOT NULL,
    busy TINYINT(1) NOT NULL DEFAULT 1,
    created_at DATETIME NOT NULL,
    updated_at DATETIME NOT NULL,

    PRIMARY KEY (id, name),
    UNIQUE KEY name_key_unique_key (name, key),
    INDEX name_index (name)
) ENGINE=MyISAM
PARTITION BY LINEAR KEY(name)
PARTITIONS 25;

毎日、各行が "name;key" のペアで構成される多数の csv ファイルを受け取るため、これらのファイルを解析し (各行に created_at と updated_at の値を追加)、値をテーブルに挿入する必要があります。この例では、「名前」と「キー」の組み合わせは一意でなければならないため、次のように挿入手順を実装しました。

CREATE TEMPORARY TABLE temp_items (
    id INT NOT NULL AUTO_INCREMENT,
    name VARCHAR(200) NOT NULL, 
    key VARCHAR(10) NOT NULL, 
    busy TINYINT(1) NOT NULL DEFAULT 1,  
    created_at DATETIME NOT NULL, 
    updated_at DATETIME NOT NULL,  
    PRIMARY KEY (id) 
    ) 
ENGINE=MyISAM;

LOAD DATA LOCAL INFILE 'file_to_process.csv' 
INTO TABLE temp_items
FIELDS TERMINATED BY ',' 
OPTIONALLY ENCLOSED BY '\"' 
(name, key, created_at, updated_at); 

INSERT INTO items (name, key, busy, created_at, updated_at) 
(
    SELECT temp_items.name, temp_items.key, temp_items.busy, temp_items.created_at, temp_items.updated_at 
    FROM temp_items
) 
ON DUPLICATE KEY UPDATE busy=1, updated_at=NOW();

DROP TEMPORARY TABLE temp_items;

先ほど示したコードで目標を達成できましたが、実行を完了するには約 48 時間かかり、これが問題です。このパフォーマンスの低下は、スクリプトが非常に大きなテーブル (2 億行) をチェックする必要があり、挿入ごとに "name;key" のペアが一意であることが原因であると思います。

スクリプトのパフォーマンスを向上させるにはどうすればよいですか?

事前にすべてに感謝します。

4

5 に答える 5

5

次の方法を使用して、挿入を高速化できます。

  1. 同じクライアントから同時に多数の行を挿入する場合は、複数の VALUES リストを指定した INSERT ステートメントを使用して、一度に複数の行を挿入します。これは、個別の単一行の INSERT ステートメントを使用するよりもかなり高速です (場合によっては何倍も高速です)。空でないテーブルにデータを追加する場合は、bulk_insert_buffer_size 変数を調整して、データの挿入をさらに高速化できます。

  2. テキスト ファイルからテーブルをロードする場合は、LOAD DATA INFILE を使用します。これは通常、INSERT ステートメントを使用するよりも 20 倍高速です。

  3. 列にはデフォルト値があるという事実を利用してください。挿入する値がデフォルトと異なる場合にのみ、値を明示的に挿入します。これにより、MySQL が実行しなければならない解析が減り、挿入速度が向上します。

参照: MySQL.com: 8.2.4.1 INSERT ステートメントの最適化

于 2016-07-23T23:15:06.490 に答える
4

名前の線形キーと大きなインデックスにより、速度が低下します。

LINEAR KEY は、挿入ごとに計算する必要があります。 http://dev.mysql.com/doc/refman/5.1/en/partitioning-linear-hash.html

file_to_process.csv のサンプル データをいくつか示していただけないでしょうか。より適切なスキーマを構築する必要があるかもしれません。

編集はより詳しく見ました

INSERT INTO items (name, key, busy, created_at, updated_at) 
(
    SELECT temp_items.name, temp_items.key, temp_items.busy, temp_items.created_at, temp_items.updated_at 
    FROM temp_items
) 

これはおそらくディスク一時テーブルを作成します。これは非常に遅いため、パフォーマンスを向上させるために使用しないでください。または、tmp-table-size や max-heap-table-size などの mysql 構成設定を確認する必要があります。構成が間違っています。

于 2013-08-11T17:02:57.917 に答える
0

Javaで考えることにより;

  • オブジェクト リストをパーティションに分割し、パーティションごとにバッチ挿入ステートメントを生成します。
  • CPU コアと利用可能な db 接続を効率的に利用します。素敵な新しい Java 機能は、並列処理を簡単に実現するのに役立ちます (parallel、forkjoin など)。または、使用している CPU コアの数で最適化されたカスタム スレッド プールを作成し、中央のブロック キューから順番にスレッドを供給することができます。バッチ挿入準備済みステートメントを呼び出す。
  • 可能であれば、ターゲット テーブルのインデックスの数を減らします。外部キーが本当に必要ない場合は、削除してください。インデックスが少ないほど、挿入が速くなります。
  • CRUD 操作以外では Hibernate の使用を避け、複雑な選択には常に SQL を記述します。
  • クエリの結合数を減らし、DB を強制する代わりに、フィルタリング、集計、および変換に Java ストリームを使用します。
  • する必要がないと思われる場合は、select と insert を 1 つの SQL ステートメントとして結合しないでください。
  • JDBC 文字列に追加rewriteBatchedStatements=trueすると、アプリと DB 間の TCP レベルの通信を減らすのに役立ちます。
  • @Transactional挿入バッチを実行し、ロールバック メソッドを自分で作成するメソッドに使用します。
于 2021-05-17T15:12:47.433 に答える
0

私が指摘したいドキュメントがあります。 Speed of INSERT Statementsです。

于 2014-02-17T09:28:03.540 に答える