没有匹配时,子查询执行缓慢

时间:2014-11-13 19:29:43

标签: mysql subquery

请注意,我已经在dba.stackexchange.com上提出了这个问题,但我想我也会在这里发布:

在MySQL中,我有两个基本表 - 帖子和关注者:

CREATE TABLE Posts (
  id int(11) NOT NULL AUTO_INCREMENT,
  posted int(11) NOT NULL,
  body varchar(512) NOT NULL,
  authorId int(11) NOT NULL,
  PRIMARY KEY (id),
  KEY posted (posted),
  KEY authorId (authorId,posted)
) ENGINE=InnoDB;

CREATE TABLE Followers (
  userId int(11) NOT NULL,
  followerId int(11) NOT NULL,
  PRIMARY KEY (userId,followerId),
  KEY followerId (followerId)
) ENGINE=InnoDB;

我有以下查询,似乎已经足够优化了:

  SELECT p.*
    FROM Posts p
   WHERE p.authorId IN (SELECT f.userId
                          FROM Followers f
                         WHERE f.followerId = 9
                      ORDER BY authorId)
ORDER BY posted
   LIMIT 0, 20

EXPLAIN输出:

+------+--------------------+-------+-----------------+--------------------+---------+---------+------------+------+--------------------------+
| id   | select_type        | table | type            | possible_keys      | key     | key_len | ref        | rows | Extra                    |
+------+--------------------+-------+-----------------+--------------------+---------+---------+------------+------+--------------------------+
|    1 | PRIMARY            | p     | index           | NULL               | posted  | 4       | NULL       |   20 | Using where              |
|    2 | DEPENDENT SUBQUERY | f     | unique_subquery | PRIMARY,followerId | PRIMARY | 8       | func,const |    1 | Using index; Using where |
+------+--------------------+-------+-----------------+--------------------+---------+---------+------------+------+--------------------------+

followerId是有效的id(意思是,它实际存在于两个表中)时,查询执行几乎是立即执行的。但是,当表中不存在id时,查询仅在7秒延迟后返回结果(空集)。

为什么会这样?有没有办法在没有匹配的情况下加速查询(无需提前检查)?

2 个答案:

答案 0 :(得分:0)

  

有没有办法加快这个查询...... ???

是。你应该做两件事。

首先,您应该使用EXISTS而不是IN(交叉引用SQL Server IN vs. EXISTS Performance)。它可以加速匹配的情况,随着数据集的增长,它会派上用场(它现在可能足够快,但这并不意味着你不应该这样做。遵循最佳实践,在这种情况下,EXISTS是比IN)更好的实践

其次,您应该稍微修改第二个表上的键。您在(userId,followerId)上使用复合键开始了一个良好的开端,但在优化此特定查询方面,您需要记住最左边的前缀" MySQL索引规则,例如

  

如果表具有多列索引,则优化程序可以使用索引的任何最左前缀来查找行。 http://dev.mysql.com/doc/refman/5.6/en/multiple-column-indexes.html

来自EXPLAIN的查询执行计划告诉您的是,SQL认为将关注者加入帖子(使用帖子上的主键)更有意义,并过滤掉该索引的给定关注者的结果。可以把它想象为"向我展示所有可能的匹配,然后将其减少到匹配followerId = {}"

的那些匹配

如果使用复合键(followerId,userId)替换followerId键,则应该能够快速放大到与给定followerID关联的用户ID,并对其进行存在性检查。

我希望我知道如何更好地解释这个...这是一个难以理解的概念,直到你有一个"啊哈!"时刻和点击。但是如果你查看索引上最左边的前缀规则,并且还将followerId上的键更改为(followerId,userId)上的键,我认为它会加快它的速度。如果您使用EXISTS而不是IN,那么即使您的数据集增长,也可以帮助您保持这种速度。

答案 1 :(得分:0)

试试这个:

SELECT p.*
FROM Posts p
inner join Followers f On f.userId = p.authorId
WHERE f.followerId = 9
ORDER BY posted
   LIMIT 0, 20