我可以通过分区或合并来加速大型 MySQL/MariaDB 连接吗?

Dio*_*lis 3 mysql mariadb join partitioning

我正在尝试将以下连接查询作为 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;
Run Code Online (Sandbox Code Playgroud)

两个连接字段都被索引。但是,连接涉及对 的完整扫描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 |                 |
+------+-------------+-----------------+--------+---------------+---------+---------+-------------------------------------+------------+-----------------+
Run Code Online (Sandbox Code Playgroud)

两个表的大小都比较大:project_commits包含50亿行和commits8.47亿行。此外服务器的内存大小相对较小(16GB)。这可能意味着索引查找会访问(不幸的是磁盘)磁盘,因此性能会受到重创。根据pmonitor run 在生成的临时表上的输出,已经运行了半天多的查询,还需要 373 小时才能完成。

/home/mysql/ghtorrent/project_commits#P#p0.MYD 6.68% ETA 373:38:11
Run Code Online (Sandbox Code Playgroud)

我是否可以通过对表进行分区以某种方式提高查询的性能,以便可以在内存中执行连接,或者强制 MySQL 执行排序合并连接?由于运行替代策略所涉及的时间可能需要数小时,我宁愿有一个建议,而不是尝试。

Rol*_*DBA 6

从 EXPLAIN 计划的外观来看,您正在对project_commits.

从查询的外观上来看,我推测有从一到多的关系commitsproject_commits。我看到的问题是您的查询正在以多对一的方式收集数据。

您也在使用LEFT JOIN.

您的查询是这样说的:

向我展示所有project_commits连接到或孤立的commits

相反,您可能希望查询显示:

显示所有project_commits相关的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;
Run Code Online (Sandbox Code Playgroud)

建议#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;
Run Code Online (Sandbox Code Playgroud)

建议 #3:添加created_at索引

如果您已经有一个索引,created_at或者您已经有一个第一列是 的复合索引created_at,请跳过此建议。

ALTER TABLE `project_commits` ADD INDEX created_at_ndx (`created_at`);
Run Code Online (Sandbox Code Playgroud)

添加索引将改变EXPLAIN执行索引扫描而不是表扫描的计划

建议 #4:改变JOIN行为

您可以通过调整优化器来操纵连接操作的样式

根据关于Block Nested-Loop 和 Batched Key Access Joins 的MySQL 文档

使用 BKA 时,join_buffer_size 的值定义了每次向存储引擎请求的密钥批次的大小。缓冲区越大,对连接操作的右手表的顺序访问就越多,这可以显着提高性能。

要使用 BKA,必须将 optimizer_switch 系统变量的 batched_key_access 标志设置为 on。BKA 使用 MRR,因此也必须打开 mrr 标志。目前,对 MRR 的成本估算过于悲观。因此,还需要关闭 mrr_cost_based 才能使用 BKA。

同一页面建议这样做:

mysql> SET optimizer_switch='mrr=on,mrr_cost_based=off,batched_key_access=on';
Run Code Online (Sandbox Code Playgroud)

试一试 !!!

注意:我不知道我的建议会有什么期望。毕竟,您LEFT JOIN就像一个迭代笛卡尔连接,具有制作以下临时表的潜力

正在查找 45.73 Quintillion 行(54 亿 X 08.47 亿)

玩得开心,让我们知道你发现了什么