在连接表列上的MySQL排序非常慢(临时表)

时间:2015-12-07 14:05:41

标签: mysql performance sorting join

我有一些表格:

object 
person 
project 
[...] (some more tables) 
type 

对象表具有所有其他表的外键。

现在我进行如下查询:

SELECT * FROM object 
LEFT JOIN person ON (object.person_id = person.id) 
LEFT JOIN project ON (object.project_id = project.id)
LEFT JOIN [...] (all other joins)
LEFT JOIN type ON (object.type_id = type.id)
WHERE object.customer_id = XXX 
ORDER BY object.type_id ASC
LIMIT 25

即使对于大型结果集,这也非常有效且快速。例如,我有90000个对象,查询大约需要3秒。结果非常大,因为表中有很多列,并且所有列都被提取。有关信息:我正在使用Symfony与Propel,InnoDB和“doSelectJoinAll”函数。

但是,如果做一个查询(按type.name排序):

SELECT * FROM object 
LEFT JOIN person ON (object.person_id = person.id) 
LEFT JOIN project ON (object.project_id = project.id)
LEFT JOIN [...] (all other joins)
LEFT JOIN type ON (object.type_id = type.id)
WHERE object.customer_id = XXX 
ORDER BY type.name ASC
LIMIT 25

查询大约需要200秒!

说明:

id  | select_type   | table     | type      | possible_keys | key       | key_len   | ref           | rows      | Extra
1   | SIMPLE    | object    | ref       | object_FI_2   | object_FI_2   | 4     | const         | 164966    | Using where; Using temporary; Using filesort
1   | SIMPLE    | person    | eq_ref    | PRIMARY   | PRIMARY   | 4         | db.object.person_id   | 1     
1   | SIMPLE    | ...       | eq_ref    | PRIMARY   | PRIMARY   | 4         | db.object...._id  | 1     
1   | SIMPLE    | type      | eq_ref    | PRIMARY   | PRIMARY   | 4         | db.object.type_id     | 1     

我在进程列表中看到,MySQL正在为连接表上的这种排序创建一个临时表。

向type.name添加索引并未提高性能。只有大约800种类型的行。

我发现很多连接和大结果都是问题所在,因为如果我只使用一个连接进行查询:

SELECT * FROM object 
LEFT JOIN type ON (object.type_id = type.id)
WHERE object.customer_id = XXX 
ORDER BY type.name ASC
LIMIT 25

它的工作速度与预期一样快。

有没有办法在包含许多连接表的大型结果集上改进此类排序查询?或者,对连接表列进行排序只是一个坏习惯,不管怎么说都不应该这样做?

谢谢

1 个答案:

答案 0 :(得分:0)

LEFT阻碍了重新排列表格的顺序。没有任何LEFT的速度有多快?你得到同样的答案吗?

LEFT可能是一个红色的鲱鱼......以下是优化程序可能会做的事情:

  1. 决定执行表格的顺序。考虑任何WHERE过滤和任何LEFTs。由于WHERE object.customer_id = XXXobject可能是最好的表格。
  2. object获取满足WHERE
  3. 的行
  4. 从其他表中获取所需的列(执行JOINs)。
  5. 根据ORDER BY **排序,见下文
  6. 发送前25行。
  7. **让我们深入研究这两个:

    WHERE object.customer_id = XXX ORDER BY object.id
    WHERE object.customer_id = XXX ORDER BY virtually-anything-else
    

    你有INDEX(customer_id),对吗?表是InnoDB,对吗?好吧,每个二级索引都隐含地包含PRIMARY KEY,就像你说INDEX(customer_id, id)一样。第一个WHERE + ORDER BY的最佳索引正是如此。它将找到XXX并扫描25行,然后停止。你可能会说步骤2,4,5混合在一起。

    第二个WHERE只是通过第4步收集所有内容。这可能是数千行。因此它可能会慢很多。

    另见article on building optimal indexes