何时使用STRAIGHT_JOIN和MySQL
我只是有一个相当复杂的查询,我正在工作,它需要8秒钟运行。 EXPLAIN显示出奇怪的表格顺序,即使FORCE INDEX提示也没有使用我的索引。 我遇到了STRAIGHT_JOINjoin关键字,并开始用它replace一些我的INNER JOIN关键字。 我注意到很大的速度提高。 最后我用STRAIGHT_JOINreplace了所有的INNER JOIN关键字,现在运行时间是0.1秒。
我的问题是你什么时候使用STRAIGHT_JOIN,什么时候使用INNER JOIN? 如果您正在编写好的查询,是否有任何理由不使用STRAIGHT_JOIN?
没有很好的理由,我不推荐使用STRAIGHT_JOIN。 我自己的经验是,MySQL查询优化器比我想要的更频繁地select一个糟糕的查询计划,但是不够经常,因此,如果您总是使用STRAIGHT_JOIN,那么您应该绕过它。
我的build议是将所有查询保留为常规JOIN。 如果您发现某个查询使用的是次优查询计划,那么我会build议先尝试重新编写或重新构build查询,以查看优化程序是否会select更好的查询计划。 另外,至less对innodb来说,确保你的索引统计数据不是过时的( ANALYZE TABLE )。 这可能会导致优化器select一个差的查询计划。 优化器提示通常应该是您的最后一招。
不使用查询提示的另一个原因是,随着时间的推移,数据分布可能会发生变化,或者随着表的增长,索引select性可能会发生变化等。 您的查询提示现在是最佳的,可能会随着时间的推移而变得不理想。 但是,由于您现在已经过时的提示,优化程序将无法调整查询计划。 如果允许优化器做出决定,则保持更灵活。
从MySQL JOIN参考 :
“STRAIGHT_JOIN与JOIN相似,不同之处在于左表总是在右表之前读取,这可以用于连接优化程序按照错误顺序放置表的情况(很less)。
MySQL在复杂查询中select连接顺序并不是必须的。 通过将复杂查询指定为straight_join,查询按照指定的顺序执行连接。 通过首先将该表设置为最小公分母并指定straight_join,可以提高查询性能。
STRAIGHT_JOIN
,使用这个子句,可以控制JOIN
顺序:哪个表在外循环中扫描,哪个表在内循环中。
这是刚刚在工作中出现的情况。
考虑三个表,A,B,C
A有3000行; B有三亿行; 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可能会selectC→B→A。 C比A小,B是巨大的,都是等高素。
麻烦的是,MySQL不一定会考虑(C.id和B.c_id)与(A.id和B.a_id)之间的交集的大小。 如果B和C之间的连接返回与B一样多的行,那么这是一个非常糟糕的select。 如果从A开始将B过滤为与A相同的行,那么这将是更好的select。
通常,您希望按照最小化结果集中的行数的顺序进行连接。 所以,从一张小桌子开始,join这样一来,所得到的连接也会变小,这是理想的。 如果从一张小桌子开始,把它连接到一张更大的桌子上,结果就像大桌子一样大,事情就变成梨形。
这是统计依赖,但。 如果数据分布改变,计算可能会改变。 这也取决于连接机制的实现细节。
我会告诉你为什么我必须使用STRAIGHT_JOIN:
- 我有一个查询性能问题。
- 简化查询,查询更加高效
- 试图找出哪个具体的部分是这个问题,我只是不能。 (2人左右加在一起很慢,而且每个人都独立快)
- 然后我用缓慢和快速的查询执行EXPLAIN(添加一个左连接)
- 令人惊讶的是,MySQL完全改变了两个查询之间的JOIN命令。
所以我强迫其中的一个连接被join,以强制先前的连接被首先读取。 这阻止了MySQL改变执行顺序,像魅力一样工作!
--use 120s, 18 million data explain SELECT DISTINCT d.taid FROM tvassist_recommend_list_everyday_diverse d, tvassist_taid_all t WHERE d.taid = t.taid AND t.client_version >= '21004007' AND t.utdid IS NOT NULL AND d.recommend_day = '20170403' LIMIT 0, 10000 --use 3.6s repalce by straight join explain SELECT DISTINCT d.taid FROM tvassist_recommend_list_everyday_diverse d STRAIGHT_JOIN tvassist_taid_all t on d.taid = t.taid WHERE t.client_version >= '21004007' AND d.recommend_day = '20170403' AND t.utdid IS NOT NULL LIMIT 0, 10000