前言
网络上有大量的资料提及将 IN 改成 JOIN 或者 exist,然后修改完成之后确实变快了,可是为什么会变快呢?IN、EXIST、JOIN 在 MySQL 中的实现逻辑如何理解呢?本文也是比较粗浅的做一些介绍,知道了 MySQL 的大概执行逻辑,也方便理解。本书绝大多数内容来自:高性能MySQL第三版(O'Reilly.High.Performance.MySQL.3rd.Edition.M),还有一部分来自于网络,还有的来自于自己的理解,以下的内容有引用的都会做标准,如有雷同,纯属巧合。
IN 改为 JOIN/EXIST
例如有如下的 IN 查询:
SELECT * FROM tbl1 WHERE col3 IN ( SELECT col3 FROM tbl2 )如果子查询 select id from t2 数据量比较大的情况下,则会很慢,从网络找找答案,就知道往往是建议修改为:
SELECT * FROM tbl1 WHERE EXISTS ( SELECT 1 FROM tbl2 WHERE tbl1.col3 = tbl2.col3 )或者改成 INNER JOIN 形式:
SELECT * FROM tbl1 INNER JOIN tbl2 ON tbl1.col3 = tbl2.col3确实这两种优化是可行的。不过总体来说更推荐 INNER JOIN,下面章节也会提及。
MySQL JOIN 语法的执行逻辑
一下内容摘抄自 高性能MySQL第三版(O'Reilly.High.Performance.MySQL.3rd.Edition.M),文章目录:Query Performance Optimization-->Query Execution Basics-->The Query optimizer Process-->MySQL's join execution strategy
INNER JOIN
简单的 JOIN 例子:
SELECT tbl1.col1, tbl2.col2 FROM tbl1 INNER JOIN tbl2 USING ( col3 ) WHERE tbl1.col1 IN ( 5, 6 );MySQL 执行的伪代码:
// WHERE tbl1.col1 IN ( 5, 6 ) 筛选出 tb11 符合条件的记录 outer_iter = iterator over tbl1 where col1 IN(5,6) outer_row = outer_iter.next while outer_row // 用 tb11 的 col3 去 tbl2 表中查询,有索引将会非常快 inner_iter = iterator over tbl2 where col3 = outer_row.col3 inner_row = inner_iter.next // 可能会命中多条数据 while inner_row output [ outer_row.col1, inner_row.col2 ] inner_row = inner_iter.next end outer_row = outer_iter.next end实际上就是两个循环啦,从上面的代码可以大致了解到,为什么等连接加了索引会很快,主要是因为加了索引,这条语句将走索引:inner_iter = iterator over tbl2 where col3 = outer_row.col3
LEFT JOIN
简单的例子:
SELECT tbl1.col1, tbl2.col2 FROM tbl1 LEFT OUTER JOIN tbl2 USING ( col3 ) WHERE tbl1.col1 IN ( 5, 6 );MySQL 执行的伪代码:
// WHERE tbl1.col1 IN ( 5, 6 ) 筛选出 tb11 符合条件的记录 outer_iter = iterator over tbl1 where col1 IN(5,6) outer_row = outer_iter.next while outer_row // 用 tb11 的 col3 去 tbl2 表中查询,有索引将会非常快 inner_iter = iterator over tbl2 where col3 = outer_row.col3 inner_row = inner_iter.next if inner_row // 可能会命中多条数据 while inner_row output [ outer_row.col1, inner_row.col2 ] inner_row = inner_iter.next end else // 没有命中的则返回 NULL output [ outer_row.col1, NULL ] end outer_row = outer_iter.next end
