飞利浦·斯塔克|庖丁解牛|图解 MySQL 8.0 优化器查询转换篇( 六 )



4 应用当前查询块转换(apply_local_transforms)
该函数在flattern subqueries之后 , bottom-up调用 , 主要分几个步骤:
删除无用列(delete_unused_merged_columns)
如果查询块已经删除了一些derived tables/views , 遍历SELECT列表的列 , 删除不必要的列
简化JOIN(simplify_joins)
该函数会把Query_block中的top_join_list的嵌套join的简化为扁平化的join list 。 嵌套连接包括table1 join table2 , 也包含table1 (table2 table3)这种形式 。 如果所示的简化过程:

分区表的静态剪枝(prune_partitions)
由于剪枝根据HASH/RANGE/LIST及二级分区都有不同 , 这里简单介绍下剪枝过程 , 现有prune_partitions是在prepare和optimize阶段会被调用 , 某些常量子查询被评估执行完 。
struct TABLE { ...... partition_info *part_info{nullptr; /* Partition related information */ /* If true all partitions have been pruned away */ bool all_partitions_pruned_away{false; ...... SQL tranformation phaseSELECT_LEX::apply_local_transforms--prune_partitionsfor example select * from employee where company_id = 1000 ;SQL optimizer phaseJOIN::prune_table_partitions--prune_partitions ------based on tbl-join_cond_optim() or JOIN::where_condfor example explain select * from employee where company_id = (select c1 from t1); 举例下面RANGE剪枝的过程: root:refCREATE TABLE R2 ( -a INT -d INT -) PARTITION BY RANGE(a) ( -PARTITION p20 VALUES LESS THAN (20) -PARTITION p40 VALUES LESS THAN (40) -PARTITION p60 VALUES LESS THAN (60) -PARTITION p80 VALUES LESS THAN (80) -PARTITION p100 VALUES LESS THAN MAXVALUE -);Query OK 0 rows affected (0.09 sec)root:refSelect * From R2 where a40 and a80; 剪枝详细过程如下: 由于剪枝需要根据不同条件产生的pruning结果进行交集 , 因此剪枝过程中需要使用read_partitions这样的bitmap来保存是否使用该对应分区 。 另外剪枝过程类似迭代判断 , 因此引入了part_iterator来保存开始、结束和当前 , 以及对应需要获取区间范围的endpoint函数和获取下一个值next的迭代器函数 。 这里巧妙的运用了指针 , 来兼容不同分区类型Hash/Range/List类型 , 如下图所示:


获取join_cond或者m_where_cond的SEL_TREE红黑树(get_mm_tree) 调用find_used_partitions来获取满足的分区 , 对于SEL_TREE的每个区间(interval):1. 获取区间的左右端点 2.从左边继续获取下一个满足的分区 , 直到到右边端点结束 , 每次调用完满足条件的分区需要使用bitmap_set_bit设置该分区在part_info-read_partitions上的位点 。find_used_partitions是根据SEL_TREE的结构进行递归 , 如图从左到右遍历next_key_part(and condition) , 然后再遍历SEL_TREE的左右(也就是上下方向 , or condition)深度递归 。(start) | $ | Partitioning keyparts $ subpartitioning keyparts | $ | ... ... $ | | | $ | +---------+ +---------+ $ +-----------+ +-----------+ \\-| par1=c1 |--| par2=c2 |-----| subpar1=c3|--| subpar2=c5| +---------+ +---------+ $ +-----------+ +-----------+ | $ | | | $ | +-----------+ | $ | | subpar2=c6| | $ | +-----------+ | $ | | $ +-----------+ +-----------+ | $ | subpar1=c4|--| subpar2=c8| | $ +-----------+ +-----------+ | $ | $ +---------+ $ +------------+ +------------+ | par1=c2 |------------------| subpar1=c10|--| subpar2=c12| +---------+ $ +------------+ +------------+ | $ ... $例如第一行(par1=c1 and par2=c2 and subpar1=c3 and subpar2=c5)的遍历的stack将是:in find_used_partitions(key_tree = \"subpar2=c5\") (***)in find_used_partitions(key_tree = \"subpar1=c3\")in find_used_partitions(key_tree = \"par2=c2\") (**)in find_used_partitions(key_tree = \"par1=c1\")in prune_partitions(...)然后是继续下面的条件 , 以此类推or(par1=c1 and par2=c2 and subpar1=c3 and subpar2=c6)or(par1=c1 and par2=c2 and subpar1=c4 and subpar2=c8)or(par1=c2 and subpar1=c10 and subpar2=c12) 下图来展示了pruning的结构和过程: 5 下推条件到Derived Table(push_conditions_to_derived_tables)