我刚刚处理了一个相当复杂的查询,并且需要8秒才能运行.EXPLAIN显示了一个奇怪的表顺序,即使使用FORCE INDEX提示我的索引也没有被使用.我遇到了STRAIGHT_JOIN join关键字并开始用它替换我的一些INNER JOIN关键字.我注意到速度提升了很多.最后我用STRAIGHT_JOIN替换了所有我的INNER JOIN关键字用于此查询,它现在在.01秒内运行.
我的问题是你什么时候使用STRAIGHT_JOIN,什么时候使用INNER JOIN?如果您正在撰写好的查询,是否有任何理由不使用STRAIGHT_JOIN?
没有充分理由我不建议使用STRAIGHT_JOIN.我自己的经验是MySQL查询优化器比我想要的更频繁地选择一个糟糕的查询计划,但通常不足以绕过它,如果你总是使用STRAIGHT_JOIN,那就是你要做的.
我的建议是将所有查询保留为常规JOIN.如果您发现一个查询正在使用次优查询计划,我建议首先尝试重写或重新构造查询,以查看优化程序是否会选择更好的查询计划.此外,至少对于innodb,请确保您的索引统计信息不仅仅是过时的(ANALYZE TABLE).这可能导致优化器选择不良的查询计划.优化程序提示通常应该是您的最后手段.
不使用查询提示的另一个原因是,随着表的增长,您的数据分布可能会随着时间的推移而发生变化,或者您的索引选择性可能会发生变化等.您的查询提示现在是最佳的,可能会随着时间的推移变得不理想.但是由于您现在过时的提示,优化器将无法调整查询计划.如果允许优化器做出决策,您将保持更灵活.
来自MySQL JOIN参考:
"STRAIGHT_JOIN类似于JOIN,除了左表总是在右表之前读取.这可以用于连接优化器以错误的顺序放置表的那些(少数)情况."
MySQL在复杂查询中选择连接顺序并不是必需的.通过将复杂查询指定为straight_join,查询按照指定的顺序执行连接.通过首先将表放在最小公分母并指定straight_join,您可以提高查询性能.
这是最近才出现的情景.
考虑三个表,A,B,C.
A有3000行; B有300,000,000行; 和C有2000行.
定义外键:B(a_id),B(c_id).
假设您有一个如下所示的查询:
select a.id, c.id from a join b on b.a_id = a.id join c on c.id = b.c_id
根据我的经验,在这种情况下,MySQL可能会选择C - > B - > A. C小于A而B是巨大的,它们都是等同物.
问题是MySQL不一定要考虑(C.id和B.c_id)vs(A.id和B.a_id)之间交集的大小.如果B和C之间的连接返回与B一样多的行,那么这是一个非常糟糕的选择; 如果从A开始将B过滤到与A一样多的行,那么它将是一个更好的选择.straight_join
可以用来像这样强制这个顺序:
select a.id, c.id from a straight_join b on b.a_id = a.id join c on c.id = b.c_id
现在a
必须加入之前b
.
通常,您希望以最小化结果集中的行数的顺序执行连接.因此,从一个小桌子开始加入,使得最终的连接也很小,是理想的选择.如果从一张小桌子开始,将它连接到一个更大的桌子,就像大桌子一样大,那就是梨形.
这是统计数据依赖.如果数据分布发生变化,则计算可能会发生变化.它还取决于连接机制的实现细节.
我在MySQL中看到的最糟糕的情况是,除了需要straight_join
或主动索引提示之外,所有这些查询都是通过光过滤以严格的排序顺序对大量数据进行分页.MySQL强烈倾向于使用索引来对任何过滤器和连接进行排序; 这是有道理的,因为大多数人并没有尝试对整个数据库进行排序,而是有一个响应查询的有限行的子集,并且排序有限的子集比过滤整个表要快得多,无论它是排序的还是不.在这种情况下,直接在具有索引列的表之后直接连接我想要对固定的事物进行排序.
STRAIGHT_JOIN
,使用此子句,您可以控制JOIN
顺序:在外循环中扫描哪个表以及在内循环中扫描哪个表.
我会告诉你为什么我必须使用STRAIGHT_JOIN:
我在查询中遇到性能问题。
简化查询,查询效率突然提高
试图弄清楚是哪个具体部分导致了问题,但我做不到。(2个左连接在一起速度很慢,每个独立连接速度都很快)
然后,我同时执行慢速查询和快速查询(添加左联接之一)
令人惊讶的是,MySQL完全更改了两个查询之间的JOIN顺序。
因此,我将其中一个连接强制为straight_join,以强制首先读取先前的连接。这阻止了MySQL更改执行顺序,并且像个魅力一样起作用!