本篇为子查询优化系列第三篇,翻译自 MariaDB 博客:https://mariadb.com/kb/en/firstmatch-strategy/,建议先看 MySQL子查询优化 和 Semi-join Materialization 子查询优化策略
实现思路
FirstMatch 是子查询 semijoin 优化的一种实现策略。下面通过一个示例来说明其原理,要查找欧洲有100万人口以上城市的国家:
select * from Country
where Country.code IN (select City.Country
from City
where City.Population > 1*1000*1000)
and Country.continent='Europe'
常规 join 的过程是下图这样的:
由于 Germany 有两个大城市(在该图中),所以联接结果中会有2个 Germany。 这是不正确的,SELECT ... FROM Country不应该得到两个相同的国家记录。FirstMatch 就是去重的一种策略,一旦找到第一个匹配项,就可以回到驱动表取下一行数据与被驱动表进行关联:
如上图,执行过程为:
- 驱动表是 country 表,从 country 表中取出一行 R;
- 从R 中取出 code 字段,到被驱动表 city 表中进行查找,只要找到第一个匹配的记录,就不再往下继续查找;
- 重复 1、2 ,直到结束。
执行成本
使用 Semi-join FirstMatch 优化策略的执行计划如下:
select * from Country where Country.code IN (select City.Country from City \
where City.Population > 1*1000*1000) and Country.continent='Europe';
+----+-------------+---------+-----------+--------------------+------+----------------------------------+
| id | select_type | table | key | ref | rows | Extra |
+----+-------------+---------+-----------+--------------------+------+----------------------------------+
| 1 | PRIMARY | Country | continent | const | 60 | Using index condition |
| 1 | PRIMARY | City | Country | world.Country.Code | 18 | Using where; FirstMatch(Country) |
+----+-------------+---------+-----------+--------------------+------+----------------------------------+
Extra 列中会有 FirstMatch(Country) 标识,总扫描行数为 60+60*18.
小结
- 在执行计划中,FirstMatch 显示在 Extra 信息中 FirstMatch(驱动表);
- 通过参数 optimizer_switch 中的 semijoin=on 和 firstmatch=on 开启 Semi-join FirstMatch,默认就是开启的。
网友评论