MariaDB 10.1.26でより複雑なクエリの一部として次の結合クエリを実行しようとしています。
select distinct
project_commits.project_id,
date_format(created_at, '%x%v1') as week_commit
from project_commits
left join commits
on project_commits.commit_id = commits.id;
両方の結合フィールドにインデックスが付けられます。ただし、結合にはproject_commits
のフルスキャンとcommits
のインデックスルックアップが含まれます。これは、EXPLAIN
の出力によって裏付けられます。
+------+-------------+-----------------+--------+---------------+---------+---------+-------------------------------------+------------+-----------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+-----------------+--------+---------------+---------+---------+-------------------------------------+------------+-----------------+
| 1 | SIMPLE | project_commits | ALL | NULL | NULL | NULL | NULL | 5417294109 | Using temporary |
| 1 | SIMPLE | commits | eq_ref | PRIMARY | PRIMARY | 4 | ghtorrent.project_commits.commit_id | 1 | |
+------+-------------+-----------------+--------+---------------+---------+---------+-------------------------------------+------------+-----------------+
2つのテーブルのサイズは比較的大きく、project_commits
には50億行、commits
847百万行が含まれています。また、サーバーのメモリサイズは比較的小さい(16 GB)。これはおそらく、インデックスルックアップが(残念ながら磁気)ディスクにヒットするため、パフォーマンスに大きな影響が出ることを意味します。生成された一時テーブルで pmonitor runの出力によると、すでに半日以上実行されているクエリは、完了するまでにさらに373時間かかります。
/home/mysql/ghtorrent/project_commits#P#p0.MYD 6.68% ETA 373:38:11
テーブルを分割してメモリ内で結合を実行できるようにするか、MySQLに sort-merge join を実行させることで、クエリのパフォーマンスを何らかの方法で向上させることができますか?代替戦略の実行にかかる時間は何時間もかかる可能性があるため、実際に試すのではなく、提案をしたいのです。
MariaDBはソート/マージ結合をサポートしていないようなので、必要なフィールドを2つのソートされたファイルにエクスポートし、UNIXでJOIN
およびDISTINCT
を実行しましたjoinおよびuniqツールを使用して、結果をデータベースにインポートします。操作は完了までに12時間もかかりませんでした。詳細は こちら です。
EXPLAINプランの外観から、project_commits
で全表スキャンを実行しています。
クエリの外観から、commits
からproject_commits
への1対多の関係があると思います。私が見ている問題は、クエリがデータを1対多に収集していることです。
LEFT JOIN
も使用しています。
あなたのクエリは言っています:
commits
に接続または孤立しているすべてのproject_commits
を表示
代わりに、クエリに次のように伝えたい場合があります。
commits
に接続されているすべてのproject_commits
を表示
EXPLAIN select distinct
project_commits.project_id,
date_format(created_at, '%x%v1') as week_commit
from commits
left join project_commits
on commits.id = project_commits.commit_id;
INNER JOIN
を使用EXPLAIN select distinct
project_commits.project_id,
date_format(created_at, '%x%v1') as week_commit
from project_commits
inner join commits
on project_commits.commit_id = commits.id;
created_at
インデックスを追加するcreated_at
のインデックスが既にある場合、または最初の列がcreated_at
の複合インデックスが既にある場合は、この提案をスキップしてください。
ALTER TABLE `project_commits` ADD INDEX created_at_ndx (`created_at`);
インデックスを追加すると、テーブルスキャンではなくインデックススキャンを実行するようにEXPLAIN
計画が変更されます
JOIN
の動作を変更するオプティマイザをテストすることにより、結合操作のスタイルを操作できます
MySQLのドキュメントによると Block Nested-Loop and Batched Key Access Joins
BKAが使用される場合、join_buffer_sizeの値は、ストレージエンジンへの各リクエスト内のキーのバッチの大きさを定義します。バッファーが大きいほど、結合操作の右側のテーブルへの順次アクセスが増えるため、パフォーマンスが大幅に向上します。
BKAを使用するには、optimizer_switchシステム変数のbatched_key_accessフラグをオンに設定する必要があります。 BKAはMRRを使用するため、mrrフラグもオンにする必要があります。現在、MRRのコスト見積もりはあまりにも悲観的です。したがって、BKAを使用するには、mrr_cost_basedをオフにする必要もあります。
この同じページはこれを行うことをお勧めします:
mysql> SET optimizer_switch='mrr=on,mrr_cost_based=off,batched_key_access=on';
注:私は自分の提案から何を期待できるかわかりません。結局のところ、あなたのLEFT JOIN
は、次のような一時テーブルを作成する可能性がある反復的なデカルト結合のようなものです
4.573十億行(54億×8億4,700万)が検索されます
楽しんで、あなたが見つけたものを私たちに知らせてください
テーブルのcommitとproject_commitsでカバリングインデックスを作成すると、正しい順序でインデックススキャンを実行できるようになります。
ALTER TABLE `commits` ADD INDEX created_at_ndx (`id`, `created_at`);
ALTER TABLE `project_commits` ADD INDEX commit_id_ndx (`commit_id`, `project_id`);
その後、あなたは行うことができます
select distinct project_id, date_format(created_at, '%x%v1') as week_commit
from (select
project_commits.project_id,
commits.created_at
from commits
inner join project_commits
on project_commits.commit_id = commits.id);
PLaneは次のようになります。
+------+-------------+-----------------+--------+------------------------+---------+---------+---------------------------------------+------------+-----------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+-----------------+--------+------------------------+---------+---------+---------------------------------------+------------+-----------------+
| 1 | PRIMARY | | ALL | | | | | ... | Using temporary |
| 2 | DERIVED | project_commits | index | commit_id_ndx | PRIMARY | 8 | | ... | Using index |
| 2 | DERIVED | commits | eq_ref | PRIMARY,created_at_ndx | PRIMARY | 4 | db_9_050611.project_commits.commit_id | ... | |
+------+-------------+-----------------+--------+------------------------+---------+---------+---------------------------------------+------------+-----------------+