为什么在mysql中这个选择查询这么慢?

时间:2017-02-03 08:37:31

标签: mysql sql

表架构

数据库引擎 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

输出

enter image description here

EXPLAIN SELECT project_id , COUNT(*) AS commit_count
FROM commits
WHERE committer_id = 30351173
GROUP BY project_id

输出

enter image description here

2 个答案:

答案 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

  • 测试slow_id
  • 在different_id上测试
  • 使用稍微不同的查询(例如添加AS ..别名)在slow_id上再次测试

第三次测试的原因是,当运行第一个查询时,MySQL还会加载池中的数据(如果使用InnoDB,你没有),也加载到RAM中,以便第二个查询可能会更快,因为池,内存和任何I / O缓存已经准备好。如果是这样,第三个查询也会运行得更快 - 如果它当然与第二个查询相当 - “这个查询很慢”问题将被揭示为工件。

但是由于查询是缓存,您希望再次获取相同的数据,而不是从缓存中提取结果。所以第三个查询需要稍微不同才能关闭缓存。否则你会看到什么似乎是一个非常快速的查询,当它实际上是什么,但是。

(1)这是你进行快速测试的。否则,性能测试比这更复杂; Percona博客有几篇关于这个主题的文章。

答案 1 :(得分:-1)

这是因为

分组

语句

SQL-Server必须遍历表中的每个sigle行。

project_id上的索引可能会解决问题。

alter table commit add index(committer_id);