同样的SQL,不同的表做驱动表可能会导致执行的不同。例如:
1) 主表为lesson:
EXPLAIN SELECT l.*
FROM lesson l
INNER JOIN lesson_collect lc ON l.lesson_id=lc.lesson_id
WHERE lc.account_id='...'
ORDER BY l.`create_time` DESC\G;
2) 主表为lesson_collect
EXPLAIN SELECT l.*
FROM lesson_collect lc
INNER JOIN lesson l ON lc.lesson_id=l.lesson_id
WHERE lc.account_id='...'
ORDER BY l.`create_time` DESC\G;
同样的语句, lc做主表能够导致两张表都使用索引,l做索引只能使用一个索引;lc做主表使用了temporary和filesort,而l做主表使用了filesort。
驱动表
MySQL优化器处理多表连接时首先要确定以谁为驱动表,也就是说以哪个表为基准,一般情况下,哪个表的结果集小,就以哪个表为驱动表。
MySQL 表关联的算法是 Next LOOP Join,是通过驱动表的结果集作为循环基础数据,然后一条一条地通过该结果集中的数据作为过滤条件到下一个表中查询数据,然后合并结果。
当进行多表连接查询时,驱动表的定义为:
1) 指定了联接条件时,满足查询条件的记录行数少的表为驱动表;
2) 未指定联接条件时,行数少的表为驱动表;
3) LEFT JOIN和straight_join中, 最左表为驱动表;
4) INNER JOIN中,MySQL优化器自动选择最小表作为驱动表。
优化的目标是尽可能减少JOIN中Nested Loop的循环次数, 使用小结果集驱动大结果集。
因此,有时候,同一SQL语句,inner join会比left join要快。原因就是inner join可以由mysql自己选择驱动表,而left join很可能被程序员定义为慢表为驱动表。
驱动表造成的问题
mysql自动选取驱动表也不一定是真正的最优方案。当SQL中没有order by时,MySQL优化器选择的驱动表一般没有问题。可当表需要字段来排序,例如create_time,如果排序字段不在驱动表里,就不可避免的出现「Using filesort」
、「Using temporary」
。
因此,要尽可能的保证排序字段在驱动表中,left join和straight_join可以强制指定连接顺序。
有时可能遇到这种问题:原本运行良好的查询语句,过了一段时间后,突然变得很糟糕。有可能是因为数据分布情况发生了变化,从而导致MySQL优化器对驱动表的选择发生了变化,进而出现索引失效的情况。