【问题标题】:MySQL sorting on joined table column extremely slow (temp table)MySQL对连接表列的排序非常慢(临时表)
【发布时间】:2016-03-12 04:04:28
【问题描述】:

我有一些桌子:

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 秒。结果非常大,因为表有很多列并且所有列都被获取。有关信息:我正在使用带有 Propel、InnoDB 和“doSelectJoinAll”功能的 Symfony。

但如果进行类似(按 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

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

有没有办法改进对包含许多连接表的大型结果集的此类排序查询?还是对连接表列进行排序只是一个坏习惯,无论如何都不应该这样做?

谢谢

【问题讨论】:

  • 使用 EXPLAIN 结果编辑您的问题
  • 好的,我为有问题的查询添加了解释结果

标签: mysql performance sorting join


【解决方案1】:

LEFT 妨碍了重新排列表格的顺序。没有LEFT,它有多快?你得到了同样的答案吗?

LEFT 可能是一个红鲱鱼......这是优化器可能正在做的事情:

  1. 决定表格的排列顺序。考虑任何WHERE 过滤和任何LEFTs。由于WHERE object.customer_id = XXXobject 可能是最好的开始表。
  2. object 中获取满足WHERE 的行。
  3. 从其他表中获取所需的列(执行JOINs)。
  4. 按照ORDER BY排序**见下文
  5. 交付前 25 行。

** 让我们深入研究这两个:

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

【讨论】:

  • 感谢您的回答。我们使用的是 Symfony 1.4,不幸的是 JOIN LEFT 是由框架自动完成的,所以我无法摆脱它。
  • 是的,我们正在使用 InnoDB,我会将这些信息添加到我的问题中。是的,我有 INDEX(customer_id),因为所有外键都有索引。我不明白如果我按 type.name 排序,为什么对象表中的 INDEX(customer_id, id) 会有所帮助?
  • 你可以使用mysql命令行工具对SELECTs进行实验,看看有无LEFT的速度有多快。
  • WHERE 子句请求 INDEX(customer_id)INDEX(customer_id, id) -- 如果您有 PRIMARY KEY(id),则相同。
  • 第一个查询可以从INDEX(customer_id, type_id) 中受益,因为它可以在索引中执行WHEREORDER BYLIMIT。第二个查询不能这样做,因为type_id 在不同的表中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-12-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-30
  • 2013-09-05
  • 1970-01-01
相关资源
最近更新 更多