web-dev-qa-db-ja.com

大規模なMySQL / MariaDBの結合を、パーティション分割またはマージによって高速化できますか?

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 を実行させることで、クエリのパフォーマンスを何らかの方法で向上させることができますか?代替戦略の実行にかかる時間は何時間もかかる可能性があるため、実際に試すのではなく、提案をしたいのです。

3

MariaDBはソート/マージ結合をサポートしていないようなので、必要なフィールドを2つのソートされたファイルにエクスポートし、UNIXでJOINおよびDISTINCTを実行しましたjoinおよびuniqツールを使用して、結果をデータベースにインポートします。操作は完了までに12時間もかかりませんでした。詳細は こちら です。

1

EXPLAINプランの外観から、project_commitsで全表スキャンを実行しています。

クエリの外観から、commitsからproject_commitsへの1対多の関係があると思います。私が見ている問題は、クエリがデータを1対多に収集していることです。

LEFT JOINも使用しています。

あなたのクエリは言っています:

commitsに接続または孤立しているすべてのproject_commitsを表示

代わりに、クエリに次のように伝えたい場合があります。

commitsに接続されているすべてのproject_commitsを表示

提案#1:テーブルの順序を反転する

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;

提案#2: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;

提案#3:created_atインデックスを追加する

created_atのインデックスが既にある場合、または最初の列がcreated_atの複合インデックスが既にある場合は、この提案をスキップしてください。

ALTER TABLE `project_commits` ADD INDEX created_at_ndx (`created_at`);

インデックスを追加すると、テーブルスキャンではなくインデックススキャンを実行するようにEXPLAIN計画が変更されます

提案#4: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万)が検索されます

楽しんで、あなたが見つけたものを私たちに知らせてください

6
RolandoMySQLDBA

テーブルの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 | ...        |                 |
+------+-------------+-----------------+--------+------------------------+---------+---------+---------------------------------------+------------+-----------------+
0
skyde