左加入表现优于内联?

时间:2008-10-09 06:01:31

标签: mysql database performance join

我一直在处理我正在处理的应用程序中的一些查询,并且遇到了一个检索更多行而不是必要的查询,结果集在应用程序代码中被删除。

将LEFT JOIN更改为INNER JOIN会将结果集修剪为所需的内容,并且可能也会更高效(因为选择的行数较少)。实际上,LEFT JOIN'ed查询的表现优于INNER JOIN'ed,只花了一半时间完成。

LEFT JOIN :(总共127行,查询花了0.0011秒)

INNER JOIN :(总共10行,查询花了0.0024秒)

(我多次运行查询,这些是平均值)。

在两者上运行EXPLAIN都没有解释性能差异:

对于INNER JOIN:

id  select_type     table   type    possible_keys   key     key_len     ref        rows     Extra
1   SIMPLE  contacts        index       NULL        name        302     NULL         235    Using where
1   SIMPLE  lists           eq_ref      PRIMARY     PRIMARY     4   contacts.list_id     1   
1   SIMPLE  lists_to_users  eq_ref      PRIMARY     PRIMARY     8   lists.id,const  1    
1   SIMPLE  tags            eq_ref      PRIMARY     PRIMARY     4   lists_to_users.tag_id   1    
1   SIMPLE  users           eq_ref      email_2     email_2     302     contacts.email 1    Using where

LEFT JOIN:

id  select_type     table   type    possible_keys   key     key_len     ref     rows    Extra
1   SIMPLE          contacts index      NULL        name        302     NULL    235     Using where
1   SIMPLE        lists     eq_ref      PRIMARY     PRIMARY     4   contacts.list_id    1    
1   SIMPLE    lists_to_users eq_ref     PRIMARY     PRIMARY     8   lists.id,const  1    
1   SIMPLE         tags     eq_ref      PRIMARY     PRIMARY     4   lists_to_users.tag_id   1    
1   SIMPLE        users     eq_ref      email_2     email_2     302     contacts.email  1   

查询本身:

SELECT `contacts`.*, `lists`.`name` AS `group`, `lists`.`id` AS `group_id`, `lists`.`shared_yn`, `tags`.`name` AS `context`, `tags`.`id` AS `context_id`, `tags`.`color` AS `context_color`, `users`.`id` AS `user_id`, `users`.`avatar` 
FROM `contacts`  
LEFT JOIN `lists` ON lists.id=contacts.list_id  
LEFT JOIN `lists_to_users` ON lists_to_users.list_id=lists.id AND lists_to_users.user_id='1' AND lists_to_users.creator='1'  
LEFT JOIN `tags` ON tags.id=lists_to_users.tag_id 
INNER JOIN `users` ON users.email=contacts.email 
WHERE (contacts.user_id='1') 
ORDER BY `contacts`.`name` ASC

(我所说的条款是'用户'表上的最后一个INNER JOIN)

如果查询产生影响,查询将在MySQL 5.1数据库上运行。

在这种情况下,有没有人知道为什么LEFT JOIN'ed查询优于INNER JOIN'ed?

更新:由于Tomalak建议我正在使用的小表使INNER JOIN变得更复杂,我创建了一个带有一些模拟数据的测试数据库。 'users'表是5000行,contacts表是~500,000行。结果是一样的(当你考虑到现在表格更大时,时间没有改变,这是令人惊讶的。)

我还在联系人表上运行了ANALYZE和OPTIMIZE。没有任何明显的区别。

6 个答案:

答案 0 :(得分:12)

如果您认为LEFT JOIN的实现是INNER JOIN +更多的工作,那么这个结果会令人困惑。如果INNER JOIN的实现是(LEFT JOIN +过滤)怎么办?啊,现在很清楚了。

在查询计划中,唯一的区别是: users ... extra:using where 。这意味着过滤。在具有内部联接的查询中有额外过滤步骤


这是一种与where子句中通常使用的不同类型的过滤。在A上创建索引以支持此过滤操作非常简单。

SELECT *
FROM A
WHERE A.ID = 3

考虑这个问题:

SELECT *
FROM A
  LEFT JOIN B
  ON A.ID = B.ID
WHERE B.ID is not null

此查询等同于内部联接。 B上没有索引可以帮助过滤操作。原因是where子句声明了连接结果的条件,而不是B上的条件。

答案 1 :(得分:6)

这可能是由于INNER JOIN必须检查两个表中的每一行,以查看列值(在您的情况下为电子邮件)是否匹配。无论如何,LEFT JOIN都将从一个表中返回。如果它已编入索引,那么它也会更快地知道该做什么。

答案 2 :(得分:4)

表基数对查询优化器有影响。我猜小桌子就像你使内连接更复杂的操作。只要您有比DB服务器愿意保留在内存中的记录更多的记录,内连接可能会开始优于左连接。

答案 3 :(得分:2)

imo你正陷入被称为过早优化的陷阱。查询优化器是疯狂的变化无常的东西。我的建议是继续进行,直到你能确定特定连接是否有问题为止。

答案 4 :(得分:0)

试试这个:

SELECT `contacts`.*, `lists`.`name` AS `group`, `lists`.`id` AS `group_id`, `lists`.`shared_yn`, `tags`.`name` AS `context`, `tags`.`id` AS `context_id`, `tags`.`color` AS `context_color`, `users`.`id` AS `user_id`, `users`.`avatar` 
FROM `contacts`  
INNER JOIN `users` ON contacts.user_id='1' AND users.email=contacts.email
LEFT JOIN `lists` ON lists.id=contacts.list_id  
LEFT JOIN `lists_to_users` ON lists_to_users.user_id='1' AND lists_to_users.creator='1' AND lists_to_users.list_id=lists.id
LEFT JOIN `tags` ON tags.id=lists_to_users.tag_id 
ORDER BY `contacts`.`name` ASC

这应该会给你额外的表现,因为:

  • 在任何“左”或“右”连接出现之前,您将所有内连接放入。这会在应用后续外连接之前过滤掉一些记录
  • “AND”运算符的短路(“AND”的顺序很重要)。如果列和文字之间的比较为false,则不会对表PKs和FKs之间的比较执行所需的表扫描

如果没有找到任何性能改进,则将所有列集替换为“COUNT(*)”并进行左/内测试。这样,无论查询如何,您将只检索1个单行和1个单列(计数),因此您可以放弃返回的字节数是导致查询缓慢的原因:

SELECT COUNT(*)
FROM `contacts`  
INNER JOIN `users` ON contacts.user_id='1' AND users.email=contacts.email
LEFT JOIN `lists` ON lists.id=contacts.list_id  
LEFT JOIN `lists_to_users` ON lists_to_users.user_id='1' AND lists_to_users.creator='1' AND lists_to_users.list_id=lists.id
LEFT JOIN `tags` ON tags.id=lists_to_users.tag_id 
祝你好运

答案 5 :(得分:-3)

LEFT JOIN返回的行数多于INNER JOIN,因为这两行不同 如果LEFT JOIN在它正在查找的表中找不到相关条目,它将返回表的NULL 但是如果INNER JOIN找不到相关条目,它根本不会返回整个行。

但是对于你的问题,你是否启用了query_cache? 尝试使用

运行查询
SELECT SQL_NO_CACHE `contacts`.*, ...

除此之外,我会用更多数据填充表格,运行

ANALYZE TABLE t1, t2;
OPTIMIZE TABLE t1, t2;

看看会发生什么。