表架构
数据库引擎 MyISAM
提交(约500,000,000行)
id(int) - PK(索引)
project_id(int) - FK [project-> id](已编入索引)
committer_id - FK [user-> id](已编入索引)
......
项目(约32,000,000行)
id(int) - PK(索引)
......
用户(约12,000,000行)
id - PK(索引)
......
解释
SELECT COUNT(*)
FROM commits
WHERE committer_id = 30351173
以上查询已在 10秒
中完成SELECT project_id , COUNT(*) as commit_count
FROM commits
WHERE committer_id = 30351173
GROUP BY project_id
但是,上述查询未在 2小时(7,200秒)内完成
project_id
中的 commits
是完整的索引列,但速度太慢。
为什么会这样?
问:如何加快第二次查询?
其他
SELECT project_id , COUNT(*) AS commit_count
FROM commits
WHERE committer_id = 1891264
GROUP BY project_id
我查询另一个commiter_id
已完成 15秒
其他 - 2
EXPLAIN SELECT COUNT(*) FROM commits WHERE committer_id = 30351173
输出
EXPLAIN SELECT project_id , COUNT(*) AS commit_count
FROM commits
WHERE committer_id = 30351173
GROUP BY project_id
输出
答案 0 :(得分:5)
你应该尝试覆盖索引,如
CREATE INDEX commits_proj_comm_cndx ON commits(committer_id, project_id);
您的查询:
project_id
committer_id
project_id
通过创建一个索引,其中常量WHERE
子句(此处为committer_id
)位于第一个位置,允许MySQL在访问表之前快速将所涉及的记录置零。一旦有了这些记录,MySQL就可以开始工作了。 但如果索引中还有用于分组的信息,即project_id
,MySQL甚至可以在访问表之前开始对数据进行分组。最后,如果所有,其他信息也出现在索引上(此处已经完成,因为它与WHERE
使用的信息相同), MySQL根本不需要访问该表。按此顺序提供此信息的索引是此查询的覆盖索引。
当然这越有利,你使用的数据越多,整个表行就越小;很明显,如果你有一个100 GB的表和一个75 GB的索引,性能增益很小。如果您拥有100 GB的表和1 GB的索引,那么您将获得巨大的胜利。特别是如果对1 GB索引的查询具有低基数(例如,提交者仅负责1%的数据)。然后你正在读取10 MB的数据而不是100 GB,并且你不会相信的性能提升。
获得索引后,您可以:
SELECT project_id , COUNT(1) AS commit_count
FROM commits
WHERE committer_id = 1891264
GROUP BY project_id
这应该只在索引上运行。
我说,尝试,因为维护索引的成本太高。很可能是你加速这个SELECT查询,代价是减慢INSERT和UPDATE,因为他们需要管理索引和表。
顺便说一下,the COUNT(1) is a personal taste - 很多人都考虑asterisks in queries as an antipattern,并且通过使用COUNT(1),查询将不会显示在grep
搜索中,允许专注于带有重要星号的查询。
此外,当您进行此类测试时,请记得执行 1 :
第三次测试的原因是,当运行第一个查询时,MySQL还会加载池中的数据(如果使用InnoDB,你没有),也加载到RAM中,以便第二个查询可能会更快,因为池,内存和任何I / O缓存已经准备好。如果是这样,第三个查询也会运行得更快 - 如果它当然与第二个查询相当 - “这个查询很慢”问题将被揭示为工件。
但是由于查询是缓存,您希望再次获取相同的数据,而不是从缓存中提取结果。所以第三个查询需要稍微不同才能关闭缓存。否则你会看到什么似乎是一个非常快速的查询,当它实际上是什么,但是。
(1)这是你进行快速测试的。否则,性能测试比这更复杂; Percona博客有几篇关于这个主题的文章。
答案 1 :(得分:-1)
这是因为
分组
语句
SQL-Server必须遍历表中的每个sigle行。
project_id上的索引可能会解决问题。
alter table commit add index(committer_id);