我正在尝试在MariaDB(MySQL)上运行一个简单的连接查询,但性能很糟糕。以下是我如何使用两个简单的Unix命令将查询时间从380小时减少到12小时以下。下面是查询,它是我使用关系OLAP框架simple-rolap实现的GHTorrent分析的一部分。selectdistinctproject_commits.project_id,date_format(created_at,'%x%v1')asweek_commitfromproject_commitsleftjoincommitsonproject_commits.commit_id=commits.id;两个连接字段都有索引。然而,MariaDB通过对project_commits进行全表扫描和对提交进行索引查找来进行连接。这可以从EXPLAIN的输出中看出。这两个表的记录比较大:project_commits有50亿行,commits有8.47亿行。服务器的内存比较小,只有16GB。所以可能是因为内存无法存储这么大的索引,需要读取磁盘,严重影响了性能。从pmonitor对临时表的分析结果来看,这个查询已经运行了半天,需要运行373个小时。/home/mysql/ghtorrent/project_commits#P#p0.MYD6.68%ETA373:38:11在我看来,这太过分了,因为排序合并连接所需的I/O时间应该比估计执行时间。我已经在dba.stackexchange.com上寻求帮助,并且已经给出了一些建议供我尝试,但我不相信他们会解决我的问题。我尝试了第一个建议,结果并不乐观。至少花了半天的时间来尝试每一个建议,然后我确定了一种我认为可以解决问题的方法。我将这两个表导出到一个文件中,使用Unix连接命令将它们连接在一起,将结果传递给uniq,删除重复的行,然后将结果导回数据库。导入过程(包括重建索引)从20点41分开始,到第二天9点53分结束。以下是具体步骤。1.将数据库表导出为文本文件。我先导出连接两个表需要的字段,按照连接字段排序。为了确保排序顺序与Unix工具的排序顺序兼容,我将字段转换为字符类型。我将以下SQL查询的输出保存到文件commits_week.txt中。selectcast(idaschar)ascid,date_format(created_at,'%x%v1')asweek_commitfromcommitsorderbycid;然后将以下SQL查询的输出保存到project_commits.txt文件中:selectcast(commit_idaschar)ascid,project_idfromproject_commitsorderbycid;这将生成以下两个文件。-rw-r–r–1ddsdds15GAug421:09commits_week.txt-rw-r–r–1ddsdds93GAug500:36project_commits.txt为了避免内存不足,我使用–quick选项运行mysql客户端,否则客户端会尝试收集输出结果前的所有记录。2.使用Unix命令行工具处理文件接下来,我使用Unix连接命令连接两个文本文件。此命令线性扫描两个文件并将具有相同第一个字段的记录分组。由于文件中的记录已经排序,所以整个过程完成得非常快,几乎是I/O的速度。我还将连接的结果传递给uniq以消除重复记录,这解决了原始查询中的不同问题。同样,在排序后的输出结果上,可以通过简单的线性扫描来完成去重。这是我运行的Unix命令。joincommits_week.txtproject_commits.txt|uniq>joined_commits.txt经过一个小时的处理,得到了想要的结果。-rw-r–r–1ddsdds133GAug501:40joined_commits.txt3.将文本文件导入回数据库***,我将文本文件导入回数据库。createtablehalf_life.week_commits_all(project_idINT(11)notnull,week_commitCHAR(7))ENGINE=MyISAM;loaddatalocalinfile'joined_commits.txt'intotablehalf_life.week_commits_allfieldsterminatedby'';结语理想情况下,MariaDB应该支持排序合并连接,并且考虑到替代策略优化器应该在运行时间过长时使用排序合并连接。但在此之前,这个问题是用70年代设计的一个Unix命令解决的。
