【问题标题】:Is there a way to force MySQL execution order?有没有办法强制 MySQL 执行顺序?
【发布时间】:2011-03-28 05:38:11
【问题描述】:

我知道我可以通过使用 FORCE INDEX (abc) 关键字来更改 MySQL 执行查询的方式。但是有没有办法改变执行顺序呢?

我的查询如下所示:

SELECT c.*
FROM table1 a
INNER JOIN table2 b ON a.id = b.table1_id
INNER JOIN table3 c ON b.itemid = c.itemid
WHERE a.itemtype = 1
  AND a.busy = 1
  AND b.something = 0
  AND b.acolumn = 2
  AND c.itemid = 123456

我使用的每个关系/约束都有一个键。如果我对此语句运行解释,我会看到 mysql 首先开始查询 c。

id    select_type    table    type
1     SIMPLE         c        ref
2     SIMPLE         b        ref
3     SIMPLE         a        eq_ref

但是,我知道按a -> b -> c 的顺序查询会更快(我已经证明了这一点) 有没有办法告诉mysql使用特定的顺序?

更新:这就是我知道a -> b -> c 更快的原因。

上述查询需要 1.9 秒才能完成并返回 7 行。如果我将查询更改为

SELECT c.*
FROM table1 a
INNER JOIN table2 b ON a.id = b.table1_id
INNER JOIN table3 c ON b.itemid = c.itemid
WHERE a.itemtype = 1
  AND a.busy = 1
  AND b.something = 0
  AND b.acolumn = 2
HAVING c.itemid = 123456

查询在 0.01 秒内完成(不使用我得到 10.000 行)。 但是,这不是一个优雅的解决方案,因为此查询是一个简化的示例。在现实世界中,我有从 c 到其他表的连接。由于HAVING 是对整个结果执行的过滤器,这意味着我会从数据库中提取比必要更多的记录。

Edit2:只是一些信息:

  • 此查询中的可变部分是 c.itemid。其他所有内容都是固定值,不会改变。
  • 索引设置良好,mysql 为我选择正确的索引
    • a 和 b 之间存在 1:n 关系(使用索引 PRIMARY)
    • b 和 c 之间存在多对多关系(使用索引 IDX_ITEMID)

关键是 mysql 应该开始查询表 a 并一直工作到 c 而不是相反。实现这一目标的任何改变。

解决方案:不完全是我想要的,但这似乎有效:

SELECT c.*
FROM table1 a
INNER JOIN table2 b ON a.id = b.table1_id
INNER JOIN table3 c ON b.itemid = c.itemid
WHERE a.itemtype = 1
  AND a.busy = 1
  AND b.something = 0
  AND b.acolumn = 2
  AND c.itemid = 123456
  AND f.id IN (
         SELECT DISTINCT table2.id FROM table1
         INNER JOIN table2 ON table1.id = table2.table1_id
         WHERE table1.itemtype = 1 AND table1.busy = 1)

【问题讨论】:

  • 您如何证明a -> b -> c 会更快?另外,你能显示你的索引吗?
  • 至于测试哪个更快,如果没有实际预加载大量数据并模拟正常负载,可能很难测试。让 MySQL 进行自己的优化通常是件好事,随着您拥有的数据越多(随着时间的推移,或者运行 ANALYZE TABLE 一次或两次),它会变得越来越准确,并且只有在遇到问题时才强制索引。但是确实存在这样的问题;我肯定遇到过 MySQL 的查询计划非常糟糕的情况,但是在强制使用某个索引并因此加入顺序之后,它运行良好且高效。
  • @thomasrutter:如果 MySQL 的优化导致 2-7 秒(取决于负载/数据量)查询,除了使用我自己的优化之外,我别无选择 :) AS Hammerite 建议:STRAIGHT_JOIN 做什么我想和 1 将查询固定一千次。

标签: mysql performance sql-execution-plan


【解决方案1】:

也许你需要使用STRAIGHT_JOIN。

http://dev.mysql.com/doc/refman/5.0/en/join.html

STRAIGHT_JOIN 与JOIN 类似,只是左表总是在右表之前读取。这可用于连接优化器以错误顺序放置表的那些(少数)情况。

【讨论】:

  • STRAIGHT_JOIN is similar to JOIN, except that the left table is always read before the right table. This can be used for those (few) cases for which the join optimizer puts the tables in the wrong order. 这正是我正在寻找的。现在我的解释显示a -> b -> c,我不需要SUBSELECT。
  • 非常好。不过奇怪的是,STRAIGHT_JOIN 只能进行内连接,而不能进行外连接。 forums.mysql.com/read.php?115,565624,565668#msg-565668 试图解释原因(最后一行),但根本不正确。优化器可以并且确实改变了一些LEFT JOINs 的顺序,正如EXPLAIN 所示,似乎没有STRAIGHT_JOIN 可以解决这些问题。
  • 哇,非常感谢!以前从未听说过,这是我 10 多岁查询的完美解决方案……
【解决方案2】:

你可以使用 FORCE INDEX 来强制执行顺序,我以前也这样做过。

如果您考虑一下,对于您选择的任何索引,通常只有一个顺序可以查询表。

在这种情况下,如果您希望 MySQL 首先开始查询 a,请确保您在 b 上强制使用的索引是包含 b.table1_id 的索引。 MySQL 只有在已经先查询了a 的情况下才能使用该索引。

【讨论】:

  • 因为在某些情况下您无法修改现有联接,但您可以添加其他条件,例如强制索引
【解决方案3】:

你可以尝试用两种方式重写

  • 将一些 WHERE 条件带入 JOIN
  • 引入子查询,即使它们不是必需的

这两件事都可能影响计划者。

不过,首先要检查的是您的 stats 是否是最新的。

【讨论】:

  • 将结果添加到 JOIN 并没有改变任何东西,但是我包含了一个子查询来减少 b 中的行数。执行计划仍然显示c -> b -> a,但这似乎有效(看看我更新的问题)。
  • 向 JOIN 添加条件不会真正改善任何事情。要观察这一点,请在查询前添加“explain extended”,然后在查询后添加“show warnings;”。显示警告会将查询显示为优化引擎“优化”。它将所有 JOIN 要求移动到最近绑定的 WHERE 子句中......
猜你喜欢
  • 2018-02-25
  • 2022-11-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-10-06
  • 2011-10-07
相关资源
最近更新 更多