web-dev-qa-db-ja.com

1,000M行のMySQLテーブルの構築

この質問は、コメントの提案に基づいて Stack Overflow から再投稿されました。重複をお詫びします。

ご質問

質問1:データベーステーブルのサイズが大きくなると、どのようにMySQLを調整してLOAD DATA INFILE呼び出しの速度を上げることができますか?

質問2:コンピューターのクラスターを使用してさまざまなcsvファイルを読み込み、パフォーマンスを向上させるか、それを強制終了しますか? (これは、ロードデータと一括挿入を使用した明日のベンチマークタスクです)

ゴール

画像検索のために特徴検出器とクラスタリングパラメータのさまざまな組み合わせを試しています。その結果、大規模なデータベースをタイムリーに構築できる必要があります。

マシン情報

マシンには256ギガのRAMがあり、データベースを分散して作成時間を改善する方法がある場合、同じ量のRAMを使用できる別の2つのマシンがありますか?

テーブルスキーマ

テーブルスキーマは次のようになります

_+---------------+------------------+------+-----+---------+----------------+
| Field         | Type             | Null | Key | Default | Extra          |
+---------------+------------------+------+-----+---------+----------------+
| match_index   | int(10) unsigned | NO   | PRI | NULL    |                |
| cluster_index | int(10) unsigned | NO   | PRI | NULL    |                |
| id            | int(11)          | NO   | PRI | NULL    | auto_increment |
| tfidf         | float            | NO   |     | 0       |                |
+---------------+------------------+------+-----+---------+----------------+_

で作成

_CREATE TABLE test 
(
  match_index INT UNSIGNED NOT NULL,
  cluster_index INT UNSIGNED NOT NULL, 
  id INT NOT NULL AUTO_INCREMENT,
  tfidf FLOAT NOT NULL DEFAULT 0,
  UNIQUE KEY (id),
  PRIMARY KEY(cluster_index,match_index,id)
)engine=innodb;_

これまでのベンチマーク

最初のステップは、一括挿入とバイナリファイルから空のテーブルへの読み込みを比較することでした。

_It took:  0:09:12.394571  to do  4,000  inserts with 5,000 rows per insert
It took: 0:03:11.368320 seconds to load 20,000,000 rows from a csv file
_

バイナリcsvファイルからデータをロードする際のパフォーマンスの違いを考慮して、最初に、以下の呼び出しを使用して100K、1M、20M、200M行を含むバイナリファイルをロードしました。

_LOAD DATA INFILE '/mnt/tests/data.csv' INTO TABLE test;_

2時間後に200M行のバイナリファイル(〜3GBのcsvファイル)のロードを強制終了しました。

そこで、スクリプトを実行してテーブルを作成し、バイナリファイルからさまざまな数の行を挿入してからテーブルをドロップしました。下のグラフを参照してください。

enter image description here

バイナリファイルから100万行を挿入するのに約7秒かかりました。次に、特定のデータベースサイズでボトルネックが発生するかどうかを確認するために、一度に1M行ずつ挿入するベンチマークを行うことにしました。データベースが約59M行に達すると、平均挿入時間は約5,000 /秒に減少しました

enter image description here

グローバルなkey_buffer_size = 4294967296を設定すると、小さいバイナリファイルを挿入する場合の速度がわずかに向上しました。下のグラフは、行数が異なる場合の速度を示しています

enter image description here

ただし、100万行を挿入した場合、パフォーマンスは向上しませんでした。

行:1,000,000時間:0:04:13.761428挿入/秒:3,940

空のデータベースとの比較

行:1,000,000時間:0:00:6.339295挿入/秒:315,492

更新

次のシーケンスを使用してデータをロードすることと、データをロードするコマンドを使用すること

_SET autocommit=0;
SET foreign_key_checks=0;
SET unique_checks=0;
LOAD DATA INFILE '/mnt/imagesearch/tests/eggs.csv' INTO TABLE test_ClusterMatches;
SET foreign_key_checks=1;
SET unique_checks=1;
COMMIT;
_
enter image description here

したがって、これは生成されるデータベースサイズの点で非常に有望に見えますが、他の設定はデータ読み込みファイル内呼び出しのパフォーマンスに影響を与えないようです。

次に、別のマシンから複数のファイルをロードしようとしましたが、ファイルのサイズが大きいために他のマシンがタイムアウトになるため、load data infileコマンドはテーブルをロックします

ERROR 1205 (HY000) at line 1: Lock wait timeout exceeded; try restarting transaction

バイナリファイルの行数を増やす

_rows:  10,000,000  seconds rows:  0:01:36.545094  inserts/sec:  103578.541236
rows:  20,000,000  seconds rows:  0:03:14.230782  inserts/sec:  102970.29026
rows:  30,000,000  seconds rows:  0:05:07.792266  inserts/sec:  97468.3359978
rows:  40,000,000  seconds rows:  0:06:53.465898  inserts/sec:  96743.1659866
rows:  50,000,000  seconds rows:  0:08:48.721011  inserts/sec:  94567.8324859
rows:  60,000,000  seconds rows:  0:10:32.888930  inserts/sec:  94803.3646283_

解決策:自動インクリメントを使用する代わりに、MySQLの外部でIDを事前計算する

でテーブルを構築する

_CREATE TABLE test (
  match_index INT UNSIGNED NOT NULL,
  cluster_index INT UNSIGNED NOT NULL, 
  id INT NOT NULL ,
  tfidf FLOAT NOT NULL DEFAULT 0,
  PRIMARY KEY(cluster_index,match_index,id)
)engine=innodb;_

sQLを使って

_LOAD DATA INFILE '/mnt/tests/data.csv' INTO TABLE test FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n';"_

enter image description here

スクリプトでインデックスを事前計算することで、データベースのサイズが大きくなるにつれて、パフォーマンスへの影響がなくなったようです。

Update 2-メモリテーブルの使用

インメモリテーブルをディスクベースのテーブルに移動するコストを考慮せずに、約3倍高速。

_rows:  0  seconds rows:  0:00:26.661321  inserts/sec:  375075.18851
rows:  10000000  time:  0:00:32.765095  inserts/sec:  305202.83857
rows:  20000000  time:  0:00:38.937946  inserts/sec:  256818.888187
rows:  30000000  time:  0:00:35.170084  inserts/sec:  284332.559456
rows:  40000000  time:  0:00:33.371274  inserts/sec:  299658.922222
rows:  50000000  time:  0:00:39.396904  inserts/sec:  253827.051994
rows:  60000000  time:  0:00:37.719409  inserts/sec:  265115.500617
rows:  70000000  time:  0:00:32.993904  inserts/sec:  303086.291334
rows:  80000000  time:  0:00:33.818471  inserts/sec:  295696.396209
rows:  90000000  time:  0:00:33.534934  inserts/sec:  298196.501594
_

データをメモリベースのテーブルにロードしてからディスクベースのテーブルにコピーする チャンク単位 クエリで107,356,741行をコピーするのに10分59.71秒のオーバーヘッドがありました

_insert into test Select * from test2;
_

100M行をロードするのに約15分かかります。これは、ディスクベースのテーブルに直接挿入するのとほぼ同じです。

18
Ben

良い質問-よく説明されています。

mySQLをチューニングしてLOAD DATA INFILE呼び出しの速度を上げるにはどうすればよいですか?

キーバッファーの設定は既に高くなっていますが、それで十分ですか?これは64ビットインストールであり(そうでない場合、最初に行う必要があるのはアップグレードです)、MSNTで実行されていないと想定しています。いくつかのテストを実行した後、mysqltuner.plの出力を確認してください。

キャッシュを最も効果的に使用するために、入力データのバッチ処理/事前ソートに利点がある場合があります( 'sort'コマンドの最新バージョンには、大規模なデータセットをソートするための多くの機能があります)。また、MySQLの外部でID番号を生成する場合は、より効率的です。

コンピュータのクラスタを使用してさまざまなcsvファイルをロードする

(再度)出力セットを単一のテーブルとして動作させたいとすると、得られる唯一の利点は、IDのソートと生成の作業を分散することです-データベースを追加する必要はありません。 OTOHがデータベースクラスターを使用している場合、競合の問題が発生します(パフォーマンスの問題以外には発生しないはずです)。

データを分割し、結果のデータセットを個別に処理できる場合は、はい、パフォーマンス上の利点が得られますが、これによって各ノードを調整する必要がなくなるわけではありません。

Sort_buffer_sizeに4 GB以上あることを確認してください。

さらに、パフォーマンスを制限する要因は、すべてディスクI/Oにあります。これに対処する方法はたくさんありますが、最適なパフォーマンスを得るために、SSD上のストライプ化されたデータセットのミラーセットを検討する必要があります。

4
symcbean
  • あなたの制限要因を考慮してください。これはほぼ確実にシングルスレッドのCPU処理です。
  • あなたはすでにそれを決定しましたload data...は挿入よりも高速なので、それを使用してください。
  • 本当に大きなファイル(行番号で)が物事を大幅に遅くすることはすでにわかっています。あなたはそれらをバラバラにしたいです。
  • 重複しない主キーを使用して、少なくともN * CPUセットをキューに入れ、100万行以下を使用します...おそらくそれ以下(ベンチマーク)。
  • 各ファイルで主キーの順次ブロックを使用します。

本当に気の利いたい場合は、マルチスレッドプログラムを作成して、単一のファイルを名前付きパイプのコレクションにフィードし、挿入インスタンスを管理できます。

要約すると、ワークロードをMySQLに調整するほど、MySQLをこれに合わせて調整することはありません。

1
Jeff Ferland