この質問は、コメントの提案、複製の謝罪に基づいて、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分かかります。これは、ディスクベースのテーブルに直接挿入するのとほぼ同じです。
id
方が速いと思います。(私はあなたがこれを探していないと思いますが)