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


18

この質問は、コメントの提案、複製の謝罪に基づいて、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ファイル)のロードを強制終了しました。

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

ここに画像の説明を入力してください

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

ここに画像の説明を入力してください

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

ここに画像の説明を入力してください

ただし、1M行を挿入してもパフォーマンスは向上しませんでした。

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

vs空のデータベースの場合

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

更新

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

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;
ここに画像の説明を入力してください

そのため、これは生成されるデータベースサイズの点では非常に有望に見えますが、他の設定はロードデータのinfile呼び出しのパフォーマンスに影響を与えないようです。

その後、異なるマシンから複数のファイルをロードしようとしましたが、ファイルのサイズが大きいために他のマシンがタイムアウトするため、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';"

ここに画像の説明を入力してください

インデックスを事前に計算するスクリプトを取得すると、データベースのサイズが大きくなるにつれてパフォーマンスヒットが解消されたように見えます。

更新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;

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


1
主キーを変更するid方が速いと思います。(私はあなたがこれを探していないと思いますが)
-DavidEG

こんにちはデビッド、コメントのおかげで、残念ながらキーがないと私たちがする必要があるクエリは十分に速くありません(プライマリキー選択の背後にあるロジックはこの投稿で概説されていますstackoverflow.com/questions/4282526/mysql-group-by-最適化
ベン

1
これはテスト用ですか?MySQL MEMORYエンジンをご覧 ください: dev.mysql.com/doc/refman/5.0/en/memory-storage-engine.htmlこれをアーキテクチャとして展開することを計画している場合、どのように計画するのか興味があります障害から回復するには、MapReduce / Hadoopで処理する方が適切なようです。
多項式

こんにちは、多項式、先端に感謝、現時点で私たちはデータベースが生成されると、それは(とにかく、現在の仕様では)あまり変化文句を言わない、異なるスケールで異なる特徴検出器をテストしている
ベン

回答:


4

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

LOAD DATA INFILE呼び出しの速度を上げるためにどのようにMySQLを調整できますか?

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

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

コンピューターのクラスターを使用して異なるcsvファイルを読み込む

(再び)出力セットを単一のテーブルとして動作させたいと仮定すると、唯一の利点は、IDのソートと生成の作業を分散することです-これは、データベースをこれ以上必要としません。データベースクラスターを使用するOTOHでは、競合の問題が発生します(パフォーマンスの問題としてのみ表示されるはずです)。

データを分割し、結果のデータセットを個別に処理できる場合は、パフォーマンス上のメリットが得られますが、これは各ノードを調整する必要性を否定するものではありません。

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

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


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

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

要約すると、ワークロードをMySQLに合わせてチューニングするのではなく、このためにMySQLをチューニングするのではありません。


-1

syntacxを正確には覚えていませんが、inno dbの場合は外部キーチェックをオフにできます。

また、インポート後にインデックスを作成することもできます。これは本当にパフォーマンスが向上する可能性があります。


インデックスの再構築を延期すると、すでにテーブルにある行の数が追加する行の数よりも大幅に少ない場合にのみパフォーマンスが向上します。
-symcbean
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.