Mysql优化器源码





handle_select(){//第1层
mysql_union();//处理union操作
mysql_select(){//第2层
JOIN::prepare(){//第3层
//初始化值并作权限校验
setup_tables_and_check_access(...){...}
//查询语句中"*"扩展为表上所有列
setup_wild(...){...}
//为列填充相应信息
setup_fields(...){...}
//setup_without_group调用setup_conds、setup_order、setup_group等函数,初始化条件、排序、分组操作各子句
setup_without_group(...){...}


if(子查询 && 首次优化&&非正常视图){
//去除in/all/any/exists类型子查询中冗余子句(orderby/distinct/groupby(无having或聚集函数)).单行子查询不处理:select * from t1 where t1.a = (<single row subquery>)
remove_redundant_subquery_clauses(select_lex *subq_select_lex){//subq_select_lex子查询语句
bool order_with_sum_func = false;
for(order *o = subq_select_lex -> join->order;o != null;o=o->next){ //子句对象的连接对象join的order子句属性
order_with_sum_func |= (*o->item)->with_sum_func;//如有聚集函数,为true
}


if(subq_select_lex->order_list.elements){
if(!order_with_sum_func)//如orderby子句不包括聚集函数,去掉orderby子句
sub_select_lex->order_list.empty();//清空排序操作的链表
}


//去掉distinct子句
if(subq_select_lex->options & SELECT_DISTINCT){
changelog |= REMOVE_DISTINCT;//记录日志
subq_select_lex->join->select_distinct=false;//变更连接对象对应的属性值
subq_select_lex->options&=~SELECT_DISTINCT;//去掉distinct子句的标识
}
//没有聚集函数和having子句,去掉groupby子句
if(subq_select_lex->group_list.elements 
&& !suq_select_lex->with_sum_func 
&& !subq_lext_lex->join->having){

ahcangelog |=REMOVE_GROUP;
subq_select_lex -> join->group_list=null;
subq_select_lex -> group_list.empty();//清空分组操作的链表
}
}
}


if(子查询 && 非正常视图){
//优化in/any/all/exists式子查询
static bool resolve_subquery(THD *thd,JOIN *join){
if(in_predicate){//子查询,in操作
//in左右操作符不匹配,报错退出
if(select_lex->item_list.elements != in_predicate->left_expr->cols()){
my_error(...);
DBUG_RETURN(TRUE);
}
}

/*满足10个条件,可以被扁平化为半连接。本函数只保留符合优化情况的,
等到JOIN.optmizer调用flatten_subqueries函数时,再进行真正优化动作。*/
if(thd->optimizer_switch_flag(OPTIMIZER_SWITCH_SEMIJOIN)
&&...){
in_predicate_embedding_join_nest = outer->resolve_nest;
// 如可用半连接优化,调用push_back保存,待flatten_subqueries被调用时执行
outer->join->sj_subselects.push_back(in_predicate);
chose_semijoin = true;// 标记
}

//如不可半连接优化,调用select_transformer进行(顶层SQL)子查询优化(IN/ALL/ANY/EXITS等类型子查询)
if(!chose_semijoin && ubq_predicate ->select_transformer(join) == item_subselect::RES_ERROR){
Item_subselect::trans_res
// IN子查询、ALL/ANY子查询调用
Item_in_subselect::select_in_like_transformer(JOIN *join,Comp_creator *func){
// 如子查询对应类不存在,创建一个对应的优化类型对象
if(!optimizer){
optimizer = new Item_in_optimizer(left_expr,this);
if(!optimizer){
goto err;
}

}


thd->lex->current_select = current->outer_select();
result = (!left_expr->fixed && left_expr->fix_fields(thd,optimizer->arguments()));


// 如IN谓词左操作数只有一列,则认为是标量IN子查询(Scalar IN Subquery)
if(left_expr->cols() == 1){
// 标量子查询优化
Item_in_subselect::single_value_transformer(join,Comp_creator *func)){

//对于非SQL顶层子查询(如有嵌套,则最外层的子句即是顶层子句)
if(!func->eqne_op() // 存在大于或小于比较操作符
&& !select_lex->master_unit()->uncacheable //非相关子查询
&& (abort_on_null || (upper_item && upper_item->top_level())
|| (!left_expr->maybe_null && !subquery_maybe_null))){
Item *subs;
if(!select_lex->group_list.elements //无group 
&& !select_lex->having // 无having
&& !select_lex->with_sum_func //无聚集函数
&& !(select_lex->next_select())
&& select_lex->table_list.elements
&& !(substype() == All_SUBS && subquery_maybe_null)) // 子查询谓词为ALL且结果可能为null,不能优化
{
if(func->I_op()){
// >= ALL、>ALL、<=ANY、<ANY,用MAX聚集函数优化
item = new Item_sum_max(*select_lex->ref_pointer_array);
}else{
item = new Item_sum_min(*select_lex->ref_pointer_array);
}

// 无论max、min,最后都是单行单列的值;subs代表优化后的子查询
subs = new Item_singlerow_subselect(select_lex);
}
else{...}


// substitution是优化后的表达式(左操作符和subs联合构成新的子表达式
substitution = func->create(left_expr,subs);
}


//上面代码发现子查询没有可优化之处,则做一些处理
if(!substitution){...}


// 执行IN向EXISTS类型转换
const trans_res retval = Item_in_subselect::single_value_in_to_exists_transformer(JOIN *join,Com_creator *func){
// 如HAVING子句、聚集函数、GROUPBY子句存在,则判断是否存在NULL IN (SELECT ...)的情况。存在则添加到HAVING子句上
if(join->having || select_lex->with_sum_func || select_lex->group_list.elements){
bool tmp;
Item *item = fun->create(expr,new Item_ref_null_helper(...));
if(!abort_on_null && left_expr->maybe_null){
// 如出现NULL IN (SELECT ...)的情况,则子查询中增加trig_cond条件。步骤:1:生成item;2:and_items添加;
item = new Item_func_trig_cond(item,get_cond_guard(0));
}


// 加入到子查询的HAVING子句上,在JOIN.optimize()中,可作为条件部分完成条件优化
select_lex->having = join->having = and_items(join->having,item);

}else{// 处理having、聚集函数、groupby子句的情况
if(select_lex->table_list.elements){ // 如有from子句
if(!abort_on_null && orig_item->maybe_null){// 如初始化表达式值可能为null
if(left_expr->maybe_null){// 如左操作符值可能为null
if(!(having = new Item_func_trig_cond(having,get_cond_guard(0)))){
DBUG_RETURN(RES_ERROR);
}
}
}

// 处理对象不同,原因和逻辑同上
if(!abort_on_null && left_expr->maybe_null){
if(!(item = new Item_func_trig_cond(item,get_cond_guard(0)))){
DBUG_RETURN(RES_ERROR);
}
}
}else{//无from子句
if(select_lex->master_unit()->first_select()->next_select()){
Item *new_having = fun->create(expr,new Item_ref_null_helper(...));


if(!abord_on_null && left_expr->maybe_null){
if(!(new_having = new Item_func_trig_cond(new_having,get_cond_guard(0)))){
DBUG_RETURN(RES_ERROR);
}
}else{// 无from子句的简单查询处理
item = func->create(left_expr,item);
}
}
}

}
}




}

}else{
// 如左操作数多列且谓词为ALL/ANY/SOME,报语法错误
if(func != &eq_creator){
my_error((ER_OPERAND_COLUMNS,MYF(0),1);
DBUG_RETURN(RES_ERROR);
}

// IN谓词左操作数多列,则认为是行式IN子查询(Row IN Subquery)
Item_subselect::trans_res(res)
Item_in_subslect::row_value_transformer(JOIN *join){
// in操作箱exists半连接转换
Item_subselect::trans_res
Item_in_subselect::row_value_in_to_exists_transformer(JOIN *join){
bool is_having_userd = (join->aving || select_lex->with_sum_func || select_lex->group_list.first 
|| !select_lex->table_list.elements);没有from子句

if(is_having_used){// 存在having、聚集函数或分组子句等
Item *item_having_part2 = 0;
for(unit i = 0;i < cols_sum; i++){
Item *item_nnull_test = new Item_is_not_null_test(this,new Item_ref(...));


if(!abort_on_null && left_expr->element_index(i)->maybe_null){// 与single_value_transformer相似但不同
if(!(item_nnull_test = new Item_func_trig_cond(item_nnull_test,get_cond_guard(i)))){
DEBU_RETURN(RES_ERROR);
}

item_having_par2 = and_items(item_having_part2,item_nnull_test);
item_having_parts->top_level_item();
}
}
}else{//无having、聚集函数或分组子句等
...
}


}
}




}
}
}
}
}
//调用split_sum_func、split_sum_func2等方法,统计orderby、having等子句中的sum操作
...
//初始化连接操作(JOIN)的相关信息
count_field_types(...);
}
JOIN::optimize(){//第3层
if(flatten_subqueries()){//把in子查询转化为半连接(半连接子查询消除,扁平化处理方式)
DBUG_RETURN(1);
}
[
bool JOIN::flatten_subqueries(){
// 第一步,从底层向上,先转换各子句中存在的子查询 
for(subq = sj_subselects.begin(),subq_end = sj_subselects.end();
subq < subq_end;
sbuq++){
// 功能限制:到mysql5.6为止,只支持in格式的子句转为半连接
DBUG_ASSSERT((*subq)->substype() == Item_subselect::IN_SUBS);


// 获得子句中的查询树
st_select_lex *child_select = (*subq)->unit->first_select();
JOIN *child_join = child_select->join;
child_select->where = child_join->conds;

// 递归处理:扁平化子句中的子查询
if(child_join->flatten_subqueries()){
DBUG_RETURN(true);
}
} //for结束


/*
第二步:对子查询进行转换:
转换前,对子查询数组(多个子查询)进行排序,方式:
1)相关子查询比不相关子查询靠前
2)有更多外表的子查询靠前
*/
my_qsort(sj_subselects.begin(),
sj_subselects.size(),sj_subselects.element_size(),
reinterpret_cast<qsort_cmmp>(subq_sj_candidate_cmp));


Prepared_stmt_arena_holder ps_arena_holder(thd);


uint table_count = tables;

//对排好序的所有子查询进行转换前的准备--条件替换
for(subq = sj_subselects.begin(); subq < subq_end; subq++){
xxx
if(replace_subconditon(this,tree,*subq,new Item_int(1),FALSE)){
DBUG_RETURN(TRUE);
}
}


// 对排好序的所有子查询进行转换
for(subq = sj_subselects.begin();subq < subq_end; subq++){
//处理IN_SUBS 类型的子查询为半连接操作,其他类型的子查询不予处理
if(convert_subquery_to_semijoin(this,*subq)){
DBUG_RETURN(TRUE);
}
}


/*第三步:对第二步中不能痛过convert_subquery_to_semijoin处理的子查询,把in转换为exists格式*/
for(subq = sj_subselects.begin(); subq < subq_end; subq++){
JOIN *child_join = (*subq)->unit->first_select()->join;


// 实现每个子句中的子查询优化
res = (*subq)->select_transformer(child_join);


// in转化为exist格式
if(replace_subcondition(this,tree,*subq,substitute,do_fix_fields)){
DBUG_RETURN(TRUE);/
}
}


}
]

// 对于查询语句中的关系(连接得到的临时中间关系)或视图对象,用mysql_derived_optimize,创建临时结果表或物化临时关系,便于后续使用
if(select_lex->handle_derived(thd->lex,&mysql_derived_optimize)){
DBUG_RETURN(1);
}


if(sel->first_cond_optimization){
//调用simplify_joins(),消除外连接和嵌套连接
if(simplify_joins(this,join_list,conds,true,false,&conds)){...}
[
static COND *
simplify_joins(JOIN *join,List<TABLE_LIST> *join_list,Item *conds,bool top, bool in_sj, Item **new_conds, uint *changelog){
while((table = li++)){ 第一部分:对外连接处理,转换外连接为内连接
if((nested_join = table->nested_join)){
if(table->join_cond()){ //如表上有连接条件表达式,消除可能的外连接
Item *join_cond = table->join_cond();
//递归调用simplify_join的conds参数 table->join_cond()
if(simplify_joins(join,&nested_join->join_list,join_cond,false,in_sj || table->sj_on_expr,&join_cond,changelog)){
DBUG_RETURN(true);
}
}


// 有嵌套连接存在,递归处理嵌套连接的情况。参数与消除外连接不用
if(simplify_joins(join,&nested_join->join,conds,top,in_sj || tale->sj_on_expr,&conds,changelog)){
DBUG_RETURN(true);
}
}else{...}


//对内表处理
if(!table->outer_join|| (used_tables & not_null_tables)){
if(table->join_cond()){
// 外连接转换为内连接,代码中的实现是把条件放到where子句的条件上
if(conds){
Item_cond_and *new_cond = static_cast<Item_cond_and*>(and_conds(conds,table->join_cond()));
}else{...}
}
}

if(!top)
continue;


// 经过上面代码处理,如表上还有表达式,则对应的是不可再转换的外连接的内表
if(table->on_expr){...}
}// 第一部分结束,遍历所有表结束


/*第二部分:可以转换为内连接的外连接全部处理完毕,扁平化可被扁平处理的连接,消除嵌套连接*/
/*没有连接条件且不是半连接的,都可以被扁平化处理*/
while((table = li++)){...}

}
}
]




// 递归调用record_join_nest_info函数,记录连接的嵌套信息到查询块(st_select_lex *select)中。
if(record_join_nest_info(select_lex,join_list)){...}


build_bitmap_for_nested_joins(join_list,0);
}//if(sel->first_cond_optimization)结束

//调用optimize_cond优化条件表达式(where子句:1:等式合并;2:常量求值;3:条件去除)
conds = optimize_cond(thd,conds,&cond_equal,join_list,true,&select_lex->cond_value);
[
Item *
optimize_cond(THD *thd,Item *conds,COND_EQUAL **cond_equal,List<TABLE_LIST> *join_list,
bool build_equalities,Item::cond_result *cond_value){

if(conds){
// 优化一:等式合并
if(build_equalities){
{
conds = build_equal_items(thd,conds,NULL,true,join_list,cond_equal);
[build_equal_items begin
Item *build_equal_items(THD *thd,Item *cond,COND_EQUAL *inherited,bool to_inherit,List<TABLE_LIST> *join_list,COND_EQUAL **cond_equal_ref){
if(cond){//调用build_equal_items_for_cond函数完成对条件表达式中相等项的合并
cond = build_equal_items_for_cond(thd,cond,inherited);

if(join_list){
while((table = li++)){ //遍历join_list上存在的表对象上的条件表达式on_expr
if(table->join_cond()){ //如表上存在连接条件,递归处理条件子句
List<TABLE_LIST> *nested_join_list = table->nested_join?&table_nested_join->join_list : NULL;

// 递归调用自己
table->set_join_cond(build_equal_items(thd,table->join_cond(),inherited, do_inherit, nested_join_list, &table->cond_equal));



}
} // while((table = li++)) end
} //if(join_list)结束


}
}
]build_equal_items end
}
}


// 优化二:常量求值
propagate_cond_constants(thd,(I_List<COND_CMP> *)0,conds,conds);
[propagate_cond_constants begin
static void
propagate_cond_constants(THD *thd,I_List<COND_CMP> *save_list,Item *and_father,Item *cond){
if(cond->type() == Item::COND_ITEM){
while((item = li++)){ //递归调用propagate_cond_constants
propagate_cond_constants(thd,&save,and_level ? cond : item,item);
}


if(and_level){
while((cond_cmp = cond_itr++)){
Item **args = cond_cmp->cmp_func->arguments();
if(!args[0]->const_item()){
// 完成field = field 到 field = const 的化简
change_cond_ref_to_const(thd,&save,cond_cmp->and_level,cond_cmp->and_level,args[0],args[1]);
}
}
}else if(and_father != cond && !cond->marker){
if(cond->type() == Item::FUNC_ITEM 
&& (((Item_func*) cond)->functype() == Item_func::EQ_FUNC
|| ((Item_func*) cond)->functype() == Item_func::EQUAL_FUNC)){
if(!(left_const && right_const) && args[0]->result_type() == args[1]->result_type()){
/*
已求知表达式含有常数,故调用resolve_const_item求解常量表达式,
然后调用change_cond_ref_to_const用刚求解的常量值(args[1])把表达式的格式变为类似field = const的样式
*/
if(right_const){
resolve_const_item(thd,&args[1],args[0]); //求解


func->update_used_table();


change_cond_ref_to_const(thd,save_list,and_father,and_father,args[0],args[1]); //变换
}else if(left_const){
resolve_const_item(thd,&args[0],args[1]};


func->update_used_tables();


change_cond_ref_to_const(thd,save_list,and_father,and_father,args[1],args[0]};
}
}//if(right_cond} end
}
}// else if(and_father != cond && !cond->marker) end
}
}
]propagate_cond_constants end


// 优化三:无用条件去除
conds = remove_eq_conds(thd,conds,cond_value);
[remove_eq_conds begin
Item *
remove_eq_conds(THD *thd,Item *cond,Item::cond_result *cond_value){
if(cond->type() == Item::FUNC_ITEM && ((Item_func*) cond)->functype() == Item_func::ISNULL_FUNC){...} // 为ODBC做特列处理


return intemal_remove_eq_conds(thd,cond,cond_value); //Scan all the condition
}
]remove_eq_conds end
[intemal_remove_eq_conds begin
static Item *
internal_remove_eq_conds(THD *thd,Item *cond,Item::cond_result *cond_value){
if(cond->type() == Item::COND_ITEM){
while((item = li++)){
// 递归处理各项中的子表达式
Item *new_item = internal_remove_eq_conds(thd,item,&tmp_cond_value);

/*
开始做各种处理,如:
1)总为true:2>1,移除
2)总为false:2<1 and col = 8 or col = 6 ==> col = 6
3)非空约束优化 
1)where not_null_col IS NULL :false
2)where not_null_col IS NOT NULL : true
*/
}
}else if(cond->type() == Item::FUNC_ITEM && ((Item_func*)cond)->functype() == Item_func::ISNULL_FUNC) 
{
...
}
}
]intemal_remove_eq_conds end
}


/(conds);
}


]




{
// 如果having不能成功合并到where子句中,则调用optimize_cond优化having子句
having = optimize_cond(thd,having,&cond_equal,join_list,false,&select_lex->having_value);
}


//全文检索优化,非查询优化重点,略过。
optimize_fts_limit_query();

// 没有group子句且调用count(*),min(),max()的情况进行优化
// 通过调用opt_sum_query对每个叶子表(单表)上满足上述条件的情况进行优化
if(tables_list && implicit_grouping){//有聚集函数但没有groupby子句,在JOIN.prepare()中implicit_grouping 为true。结果为0或1行。
if((res = opt_sum_query(thd,select_lex->leaf_tables,all_fields,conds))){
[opt_sum_query begin
int opt_sum_query(THD *thd,TABLE_LIST *tables,List<Item> &all_fields,Item *conds){
List_iterator_fast<Item> it(all_fields);

// 统计所有叶子节点上的单表,如做笛卡尔集,其最大的连接后的行数
// 尽管带有MAX函数且没有groupby子句,但不支持如下形式:带有外连接且where where子句的条件限制了内表,例:SELECT MAX(t1.a FROM t1 LEFT JOIN t2 join-condition WHERE t2.field IS NULL;
for(TABLE_LIST *tl = tables;tl;tl = tl->nest_leaf){...} //尽量利用存储引擎功能完成多表连接后笛卡尔集的行数计算

//遍历传入的每个列对象,如果列上存在count/min/max函数,则尽可能优化
while((item = it++)){
if(item->type() == Item::SUM_FUNC_ITEM){ //如是聚集函数,则优化
Item_sum *item_sum = (((Item_sum*)item));
switch(item_sum->sum_func()){
case Item_sum::COUNT_FUNC: //聚集函数中的count函数
// 没有where子句、函数参数不可为null(count(null))、不存在外连接、能够精确计算,则调用get_exact_record_count函数求精确值
if(!conds && !((Item_sum_count*) item)->get_arg(0)->maybe_null && !outer_tables && maybe_exact_count){
if(!is_exact_count){
if((count = get_exact_record_count(tables)) == ULONGLONG_MAX)...
}
}else if(...){//如果是全文检索(有where子句等)需要计数,求count值
count = fts_item->get_count();
}else{
count_result = 0 ;
}
break;


case Item_sum::MIN_FUNC://聚集函数中的MIN函数


case Item_sum::MAX_FUNC://聚集函数中的MAX函数{
int is_max = test(item_sum->sum_func() == Item_sum::MAX_FUNC);


Item *expr = item_sum->get_arg(0);

/*
调用find_key_for_maxmin查看是否可以利用索引得到max/min的值,如能用则加快执行速度
*/
if(expr->real_item()->type == Item::FIELD_ITEM){
if(table->file->inited || (outer_tables & table->map) 
|| !find_key_for_maxmin(is_max,&ref,item_field->field,conds,
&range_fl,&prefix_len)){
const_result = 0;
break;
}
}


error = is_max ? 
get_index_max_value(table,&ref, range_fl):
get_index_min_value(table, &ref, item_field, range_fl, prefix_len);
}else if(!expr->const_item() || conds || !is_exact_count){
//min/max不是基于列对象,如SELECT MAX(1) FROM table ...
const_result = 0;
break;
}


break;
}


default:
const_result = 0;
break;
}
}else ...
} //while语句结束
...
]opt_sum_query end


}
}


//调用make_join_statistics函数,完成多表连接,构造出查询执行计划
if(make_join_statistics(this,select_lex->leaf_tables,conds,&keyuse,first_optimization)){
[make_join_statistics begin
/*
主要过程:
1)初始化JOIN的数据结构,建立表之间的依赖关系
2)基于连接信息更新依赖关系
3)获取索引信息
4)基于表的依赖关系选出半连接的表
5)选出常量表(包括0、1行和因依赖关系导致一些表在连接关系的作用下也可以认为是常量的表),并获取真实数据。 
6)为非长量表计算行数
7)计算潜在的半连接物化操作的花费。
8)基于统计信息求解最好的连接顺序花费
*/
/**
make_join_statistics是为表确认可用索引的总入口,通过找出所有可用索引为求解最优查询计划做了铺垫
*/
static bool
make_join_statistics(JOIN *join,TABLE *tables_arg,Item *conds, key_user_array *keyuser_array, bool first_optimization){
// 初始化要连接的表的数据结构,并求解表之间的依赖关系
// 所有的叶子节点(基本表)被置于join->best_ref数组
for(s = stat, i = 0;tables;s++, tables = tables->next_leaf,i++){...}


/*
基于连接信息更新依赖关系。如存在外连接,分两步处理(两个循环体):
1)使用Warshall算法(求二元关系传递闭包的一种高效算法),构建“表”的“被依赖关系”的传递闭包;这有助于加快含有外连接的多种情况的搜索,便于快速淘汰不合法关系的连接引用。依赖关系被保存在JOIN_TAB 的dependent成员上
2)使用上步构造的传递闭包,检查外连接中不合法的连接
*/
if(join->outer_join){...}


if(conds || outer_join){ //调用update_ref_and_keys获取索引(key)信息
if(update_ref_and_keys(thd,keyuser_array, stat, join->tables, conds, join->cond_equal, ~outer_join, join->select_lex, &sargables)){
goto error;
}
[add_key_fields begin (update_ref_and_keys主要调用的函数)
static void 
add_key_fields(JOIN *join,Key_field **key_fields,uint *and_level,Item *cond,table_map usable_tables,SARGABLE_PARAM **sargables){
if(cond->type() == Item_func::COND_ITEM){
/*如果条件表达式是AND类型(如a AND b AND c),则递归调用自己遍历每个子条件(如a可以用d OR e OR f 表示,故需要递归调用自己)*/
if(((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC){
while((item = li++)){
add_key_fields(join,key_fields,and_level,item,usable_tables,sargables);
}
}else{ //条件表达式非AND类型,则递归调用自己处理OR类型
...
}


return;
}if(cond->type() == Item_func::COND_ITEM) end
}


/*
子查询的优化:处理一种子查询的情况,考虑之前的函数所进行的子查询优化(即考虑已经被下压到子查询的、被打包存入Item_func_trig_cond类的条件,其类型是 TRIG_COND_FUNC,为这样条件上的列找出索引信息)
*/
{
if(cond->type() == Item::FUNC_ITEM && ((Item_func*)cond)->functype() == Item_func::trIG_COND_FUNC){
Item *cond_arg = ((Item_func*)cond)->arguments()[0];
if(!join->group_list && !join->order && //查询语句无分组也无排序子句
join->unit->item &&
join->unit->item->substype() = Item_subselect::IN_SUBS && //子查询是IN类型的子查询
!join->unit->first_select()->next_select()){
KEY_FIELD *save = *key_fields;
add_key_fields(join,key_fields,and_level,cond_arg,usable_tables,sargables);
}


return;
}
} //满足以上注释中说明的条件,才可以找其上是否有索引可以用来做优化

...
switch(cond_func->select_optimize()){
case Item_func::OPTIMIZE_NONE:
break;
case Item_func::OPTIMIZE_KEY:{
// 化简between操作:a BETWEEN low AND hight 化简为 a >= low AND a <= hight
if(cond_func->functype() == Item_func::BETWEEN){
...
}else{ //IN,NE
is_local_field(cond_func->key_item()) && !(cond_func->used_tables() & OUTER_REF_TABLE_BIT)){
...
}
}
break;
case Item_func::OPTIMIZE_OP:
case Item_func::OPTIMIZE_NULL:
/*以上3种情况,需要调用add_key_equal_fields函数 (add_key_equal_fields函数调用了add_key_field函数)*/
case Item_func::OPTIMIZE_EQUAL:
// 处理field1 = const的情况,这时,调用add_key_field查看是否有索引在field1上可用
if(const_item){
while((item = it++)){
add_key_field(key_fields,*and_level,cond_func,item->field,TRUE,&const_item, 1, usable_tables, sargables);
[add_key_field begin
static void // add_key_field函数把相关信息保存在key_fields数组中,所以本函数的语句体是多个赋值语句 
add_key_field(KEY_FIELD **ke_fields, unit and_level, Item_func *cond, Field *field, bool eq_func, Item **value, uint num_values, table_map usable_tables,SARGABLE_PARAM **sargables){
...
/*Store possible eq field*/
(*key_fields)->field = field;
(*key_fields)->eq_func = eq_func;
(*key_fields)->val = *value;
(*key_fields)->level = and_level;
(*key_fields)->optimize = exists_optimize;
(*key_fields)->null_rejecting = ((cond->functype() == Item_func::EQ_FUNC ||
cond->functype() == Item_func::MULT_EQUAL_FUNC) &&
((Item_field*)*value)->field->maybe_null());
(*key_fields)->cond_guard = NULL;
(*key_fields)++;
}
]add_key_field end

}else{ //处理field1 = field2的情况,这时,调用add_key_field查看是否有索引在field1和field2上可用
add_key_field(key_fields, *and_level, cond_func, field, TRUE, (Item **)&item, 1,usable_tables, sargables);
}
break;
}
}
]add_key_fields end 
}


/*
基于表间的依赖关系(SQL语句表明的语法上表之间的连接顺序上的依赖),可以把一些用半连接计算的表拉到上层,
避开用半连接计算(注意有可能更改语义,使得不相关子查询变成相关子查询,这样,使得本可能利用“物化”或“松散索引扫描”变为不能使用)
*/
if(first_optimization){
/*pull_out_semijoin_tables基于功能依赖,从半链接中拉出一些表作为引用关系存在(引用关系相当于指针,使得原位置不存放对象,而是一个地址似的指针,用eq_ref指向真正对象)
什么是“功能依赖”呢,如下代码:
WHERE eo IN(SELECT it1.primary_key WHERE p(it1,it2)...)
it1.primary_key = oe 表示功能依赖,表it1可以被“拉出”,因为主键能保证it1.primary_key = oe 式的操作能用eq_ref数据访问方式被访问到。
该函数比较简单,略。
*/
if(pull_out_semijoin_tables(join)){
DBUG_RETURN(true);
}
}


/*
找出JT_SYSTEM、JT_CONST长量表。此处有多个独立循环,主要调用了set_position函数。
*/
{
...
// 对此调用set_positiion函数,每加入一个新的长量表,旧的常量表尽量往join->best_ref的后面放
set_positiion();
...
[set_positiion begin
static void 
set_positiion(JOIN *join,unit idx,JOIN_TAB *table,Key_use *key){
//为join->positions[idx]上的各成员保存值
...
join->positions[idx].records_read = 1.0; /*This is a const table*/
...
join->positions[idx].use_join_buffer = FALSE;


// 把常量表放到idx指定位置
JOIN_TAB **pos = join->best_ref + idx + 1;
JOIN_TAB *next = join->best_ref[idx];
for(;next != table;pos++){
JOIN_TAB *tmp = pos++){
JOIN_TAB *tmp = pos[0];
pos[0] = next;
next = tmp;
}


join->best_ref[idx] = table;
}


}
]set_positiion end 
}


//验证长量表的元组数,如果长量表的元组个数大于1,报错。读取长量表真实数据
for(POSITION *p_pos = join->positions, *p_end = p_pos + cons_count;p_pos<p_end;p_pos++){
...
if((tmp = join_read_const_table(s, p_pos)))
......
}


// 计算每个表的元组数
{
for(s = stat;s<stat_end;s++){
//如果是长量表,元组数至多是1
if(s->type == JT_SYSTEM || s->type == JT_CONST){
//长量表赋值:元组数、时间花费、最坏搜索因子
s->found_records = s->records = s->read_time - 1;s->worst_seeks = 1.0;
continue;
}


//计算非长量表的元组数、获取数据的时间花费、最坏搜索因子
s->found_records = s->records = s->table->file->stats.records;
s->read_time = (ha_rows) s->table->file->scan_time();
s->worst_seeks = min((double)s->found_records/10,(double)s->read_time *3);

if(s->worst_seeks<2.0){
s->worst_seeks = 2.0;
}


// 如有groupby、distinct子句,则为这些操作所在的列确定是否有索引可用
add_group_and_distinct_keys(join,s);


// 如果可以执行范围扫描,则从新计算元组数、获取数据的时间花费;否则,执行全表扫描。
TABLE_LIST *const tl = s->table->pos_in_table_list;
if(!s->const_keys.is_clear_all() &&
(!s->const_keys.is_clear_all()&&
(!tl->embedding ||
(tl->embedding && tl->embedding->sj_on_expr))){ //有索引
records = get_quick_record_count(thd, select,s->table,&s->const_keys,join->row_limit);
[get_quick_record_count begin test_quick_select是主要实现函数
static ha_rows get_quick_record_count(THD *thd,SQL_SELECT *selet,TABLE *table,const key_map *keys,ha_rows limit){
...
if(select){
if((error = select->test_quick_select(thd,*(key_map *)keys,(table_map)0,limit,0)) == 1){...
[test_quick_select begin
int SQL_SELECT::test_quick_select(THD *thd,key_map keys_to_use,
table_map prev_tables,
ha_rows limit,bool force_quick_range,
const ORDER::enum_order interesting_order){
//初始的花费计算
scan_time = records * ROW_EVALUATE_COST + 1;
read_time = head->file->scan_time() + scan_time + 1.1;
if(head->force_index){
sacn_time = read_time = DBL_MAX;
}

if(limit <records){
read_time = (double)records + sacn_time + 1; //Force to use index
}else if(read_time <= 2.0 && !force_quick_range){
DBUG_RETURN(0); /*No need for quick select*/
}


if(!keys_to_use.js_clear_all()){
...
{
...
/*根据只读索引计算扫描花费,不用读取表数据即可完成,所以花费单独计算*/
if(!head->covering_keys.is_clear_all()){
int key_for_use = find_shorttest_key(head,&head->covering_keys);
double key_read_time = param.table->file->index_only_read_time(key_for_use,rows2double(records)) + records * ROW_EVALUATE_COST;


bool chosen = false;
if(key_read_time < read_time){
read_time - key_read_time;
chosen = true;
}
}
}


...
}
...
//计算单表上带有MIN/MAX聚集函数的分组操作花费
group_trp = get_best_group_min_max(&param,tree,best_read_time);
if(group_trp){
param.table->quick_condition_rows = min(group_trp->records,head->file->stats,records);


if(group_trp->read_cost best_read_time){
best_trp = group_trp;
best_read_time = best_trp->read_cost;
}else{
grp_summary.add("chosen",false).add_alnum("cause","cost");
}


if(tree){
...
{
/*基于索引做范围扫描的最优花费*/
if((range_trp = get_key_scans_params(&param,tree,FALSE,TRUE,best_read_time))){
check_quick_select(); //计算基于索引做范围扫描的花费
[check_quick_select begin
static ha_rows check_quick_select(PARAM *param,uint idx,bool index_only,
SEL_ARG *tree,bool update_tbl_stats,
uint *mrr_flags,uint *bufsize,Cost_estimate *cost){
/*传递信息到seq,通过seq把check_quick_select 的参数带入multi_range_read_info_const方法*/
seq.keyno = idx;
seq.real_keyno = keynr;
set.start = tree;
...


/*确定是使用索引的统计信息(index statistics)还是索引的精确信息(index dives)*/
param->use_index_statistics = eq_ranges_exceeds_limit(tree,&range_count,
param->thd->variables.eq_range_index_dive_limit);


param->is_ror_scan = TRUE; //基于ROR进行扫描
/*HA_KEY_SCAN_NOT_ROR,表示索引扫描不能返回按rowid有序的记录;如无序的Hash索引就不能做到保证索引扫描按某种顺序返回值*/
if(ifle->index_flags(keynr(keynr,0,TRUE)&HA_KEY_SCAN_NOT_ROR){
param->is_ror_scan = FALSE;
}
...

if(param->order_direction != ORDER::ORDER_NOT_RELEVANT){
*mrr_flags != HA_MRR_SORTED;/*对索引的扫描采用MRR方式(优化外存的读取,使得批量读取的读取位置有序,保证磁盘磁头不是无序运动)*/
}


//根据索引利用MRR技术,获取行数、花费等信息
rows = file->multi_range_read_info_const(keynr,&seq_if,(void*)&seq,0,bufsize,mrr_flags,cost);


}
]check_quick_select end
best_trp = range_trp;
best_read_time = best_trp->read_cost;
}


if((thd->lex->sql_command != SQLCOM_DELETE)&& //非删除命令
thd->optimizer_switch_flag(OPTIMIZER_SWITCH_INDEX_MERGE)&&
interesting_order != ORDER::ORDERDESC) //不支持降序的情况
{
//基于覆盖"ROR交操作"算法求解非覆盖"ROR交操作"
if((rori_trp = get_best_ror_intersect(&param,tree,best_read_time))){
best_trp = rori_trp;
best_read_time = best_trp->read_cost;
}
}
}


//计算基于index merge的ROR-UNION操作的花费
if(!tree->merges.is_empty()){
// Cannot return rows in descending order.
if(thd->optimizer_switch_flag(OPTIMIZER_SWITCH_INDEX_MERGE) &&
interesting_order != ORDER::ORDER_DESC &&
param.table->file->stats.records){
while((imerge = it++)){
new_conj_trp = get_best_disjunct_quick(&param,imerge,best_read_time);
}


if(best_conj_trp){
best_trp = best_conj_trp
}
}
}
}
}

}
]test_quick_select end
}
...
}
}
]get_quick_record_count end

}else{ //全表扫描
Opt_trace_object(trace,"table_scan").add("rows",s->found_records).add("cost",s->read_time);
}
}//for(s = stat;s<stat_end;s++)结束
} //计算每个表元组结束


// 为利用好索引,对索引对象Key_use上的值进行更新
if(!join->plan_is_const()){
optimize_keyuse(join,keyuse_array);
}


//使用“物化”优化半连接嵌套
if(optimize_semijoin_nests_for_materialization(join)){
DBU_RETURN(true);
}
[optimize_semijoin_nests_for_materialization begin
static bool optimize_semijoin_nests_for_materialization(JOIN *join){
...
while((sj_nest = sj_list_it++)){ //遍历半连接的表
// 如果允许使用“物化”,计算物化花费
if(join->thd->optimizer_switch_flag(OPTIMIZER_SWITCH_MATERIALIZATION)){
semijoin_types_allow_materialization(sj_nest); //semijoin_types_allow_materialization主要判断是否可以物化


if(!sj_nest->nested_join->sjm.scan_allowed && !sj_nest->nested_join->sjm.lookup_allowed){
continue;
}


/*求解半连接相关表的最优连接路径(将求得一个局部连接的最优路径,因为半连接操作可能只是一个查询的一部分*/
if(Optimize_talbe_order(join->thd,join,sj_nest).choose_table_order()){DBUG_RETURN(true);}

// 计算物化花费
calculate_materialization_costs(join,sj_nest,n_tables,&sj_nest->nested_join->sjm);
}
}
DBUG_RETURN(false);
}
]optimize_semijoin_nests_for_materialization end

/*
选择多表连接算法(1,用户指定次序;2,采用贪婪穷尽式算法),进行多表连接优化,可得到最优或较优的连接路径
*/
if(Optimize_table_order(thd,join,NULL).choose_table_order()){
[choose_table_order begin
bool Optimize_table_order::choose_table_order(){
//全是长量表不用做多表连接的优化
if(join->const_tables == join->tables){
memcpy(join->best_positions,join->positions,sizeof(POSITION) *join->const_tables);
join->best_read = 1.0;
join->best_rowcount = 1;
DBUG_RETURN(false);
}


// 确定是否采用SQL语句指定的连接顺序执行多表连接查询计划的生成
/*如果在SQL语句中通过hit指定STRAIGHT_JOIN,则join->select_options被赋SELECT_STRAIGHT_JOIN值*/
const bool straight_join = test(join->select_options & SELECT_STRAIGHT_JOIN);
table_map join_tables;


if(emb_sjm_nest){ //如存在半连接,通过物化方式优化
merge_sort(join->best_ref + join->const_tables,join->best_ref + join->tablesw,
Join_tab_compare_embedded_first(emb_sjm_nest));
join_tables = emb_sjm_nest->sj_inner_tables;
}else{ //否则,需要对多表连接进行优化求解最优查询路径
if(straight_join){ //使用用户指定的表连接次序对要连接的表排序,长量表除外
merge_sort(join->best_ref + join->const_tables,
join->best_ref + join->tables,Join_tab_compare_straight());
}else{
/*否则用greed_search算法,但之前需要对连接的表排序:按元组数从小到大(启发式规则)*/
merge_sort(join->best_ref + join->const_tables,
join->best_ref + join->tables,Join_tab_compare_default());

// 所有基表去掉常量表就是用于连接的表
join_tables = join->all_table_map &~join->const_table_map;
}


if(straight_join){
optimize_straight_join(join_tables); //方式一:强制优化器使用sql中指定的表连接次序进行多表连接
}else{
if(greed_search(join_tables)){DBUG_RETURN(true);} //方式二:贪婪算法
}


if(fix_semijoin_strategies()){DBUG_RETURN(true);} //修改半连接策略并估算花费

DBUG_RETURN(true);


}
}
]choose_table_order end


[determine_search_depth begin 
/*该方法被Optimize_table_order的构造器调用,在生成优化器的多表连接对象时即决定了搜索深度*/
/*决定多表连接搜索空间的搜索深度(为greed算法准备搜索深度,greedy_search函数递归调用自己,做深度优先遍历,逐层构建连接路径)。
table_count参数和unit max_tables_for_exhaustive_opt变量决定了搜索深度,体现出了贪婪和穷尽两种思维方式。*/
uint Optimize_table_order::determine_search_dept(uint search_depth, uint table_count){
//如果指定深度,则使用指定值
if(search_dept > 0){
return search_depth;
}


const uint max_tables_for_exhaustive_opt = 7; //最大搜索深度定义为一个常数,值为7

if(table_count <= max_tables_for_exhaustive_opt){ //表的个数不包括常量表
search_depth = table_count + 1; //对象个数较少的情况,使用穷举的思路
}esle{
search_depth = max_tables_for_exhaustive_opt; //对象个数较多的情况使用非穷举的思路
}
}
]determine_search_depth end 

[optimize_straight_join begin
void Optimize_table_order::optimize_straight_join(table_map join_tables){
uint idx = join>const_tables; //得到常量表的个数

/*
join->best_ref + idx 表示从常量表之后开始的连接语义中的其他表,为每个表求最优路径(常量表不用考虑:元组数和其他表元组做连接都固定)
*/
for(JOIN_TAB **pos = join->best_ref + idx;(s = *pos);pos++){
POSITION *const position = join->positions + ids; //idx自增,position指向的位置后移


//计算表s的访问方式、花费等,结果保存在position
best_access_path(s,join_table,idx,false,record_count,position,&loose_scan_pos);
[best_access_path begin
void Optimize_table_order ::best_access_path(){
JOIN_TAB *s, //将被连接的表
table_map remaining_tables, // 没有被连接到连接树的表
unit idx,/*表的连接位置(如果存在多表连接,则要确定每个表在连接路径上的连接位置即连接次序,存储在join->positions数组上)*/
bool disable_jbuf, //如果不适用连接缓存,值为true。
double record_count, //局部查询计划估算的元组数
POSITION *pos, //表s的访问路径,即生成的新连接树是join->positions[idx]
POSITION *loose_scan_pos) //表s使用loosescan的访问路径
}{
/*第一部分:检索索引
尽量用索引完成表连接优化,避免做全表扫描。
对于指定的连接表(JOIN_tab *s)找出其具体的表(TABLE *table=>s-table),遍历连接表上可以使用的索引keyuse(s->keyuse),找出在使用了key或keypart(利用单键索引或联合索引中的部分键值可以快速读取记录)与先前的局部最优连接路径 进行连接的最下情况,如代码中得到的记录数(tmp2)用作估算最小的扫描花费。最小值应对应的情况就是最优路径*/
if(unlikely(s->keyuse != NULL)){
for(Key_use *keyuse = s->keyuse;keyuse->table == table;)){ //遍历表s上的做引
const unit key = keyuse->key;


/*遍历索引键的子键(结合循环,分析prev_recordreads求解的意义),找出表上可用的索引(这个索引使得表s与局部最优查询执行计划连接后最优)*/
while(keyuse_>table == table && keyuse->key = key){
// 找出每个子键的可访问方式
for(;keyuse_table == table && keyuse->key == key && keyuse->keypart == keypart;++keyuse){
// 如果可用的索引键没有引用连接树前面的表,则这个索引键可能可用于本表
if(!(remaining_tables & keyuse->used_tables) && !(ref_or_null_part && (keyuse->optimize & KEY_OPTIMIZE_REF_OR_NULL))){
// 获取idx参数的连接的局部查询计划的元素数,作为局部查询计划的元组数
double tmp2 = prew_record_reads(join,idx,(found_ref | keyuse->used_tables));

if(tmp2 < best_prev_record_reads){ //保存最优值和最优值对应的索引键
best_part_found_ref = keyuse->used_tables & ~join->const_table_map;
best_prev_record_reads = tmp2;
}
}
}

found_ref |= best_part_fount_ref;//如果找到最优的可用索引,记录下来
}// while(keyuse_>table == table && keyuse->key = key) 循环结束


if(ft_key){ //特殊处理全文检索
...
}else{
/*关键求解过程:
1.如果找到索引的全部键(用户使用了索引的全部键)
1.1等值计算
tmp = prev_record_reads(join,idx,found_ref);
records = 1.0;
1.2非等值计算
1.2.1存在常亮,格式为:file<op>常量(不需要考虑其他对象)
if(table->quick_keys.is_set(key)){
records = (double)table->quick_rows[key];
}else{
records = (double)s->records/rec;quick_range_couldn't use key!
}
1.2.2非常量形式(需要考虑其他对象,即引用ref其他对象的列)
1.3如果能利用覆盖索引则利用覆盖索引(调用index_only_read_time方法)
2,如果找到索引的部分键(用户使用了索引的部分键)
2.1如果可以使用大部分或唯一键
tmp = (previous record count) *(records / combination)
处理了各种情况,如ref、ref_or_null等情况
2.2否则,不做额外处理
tmp = best_time;
*/
}/* not ft_key */

{// 保存求解得到的最好的值
const double idx_time = tmp + records * ROW_EVALUATE_COST;
trace_access_idx.add("rows",records).add("cost",idx_time);
if(idx_time<best_time){
best_time = idx_time;
best = tmp;
best_records = records;
}
}
} //结束对表s上的索引的遍历 for(Key_use *keyuse = s->keyuse;


records = best_records;
} //结束对索引的判断,if(unlikely(s->keyuse != NULL))


// 以上是根据存在的索引,找出对连接有用的信息。尽量利用索引


// 第二部分:利用索引,尽量避免全表扫描


// ref访问方式如果比表扫描(或索引扫描或快速扫描)得到更少的元素
// 而且ref访问方式的花费更小,则不用进行全表扫描
if(!(records >= s->found_records || best > s->read_time)){
//"scann" means(full)index scan or (full)table scan.
...
goto skip_table_scan; //跳过表扫描
}


// 可以使用索引的范围扫描执行;
// 引用访问,可以使用同样的带有相同或更多键部分的索引
if((s->quick && best_key && s->quick->index == best_key->key && best_max_key_part >= s->table->quick_key_parts[best_key->key])){
goto skip_table_sacn; //跳过表扫描
}


// 多种情况,跳过表扫描


// 第三部分:全表扫描的情况,估算花费
{
// 启发式规则:如果有过滤条件,假设有25%的元组被过滤
if(found_constraint){
rnd_records -= rnd_records/4;
}


/*启发式规则:快速估算法估算的元组数与从表上扫描的元组数不同,则采用快速估算法得到的元组数。这个元组数更精确,其值来自存储引擎。*/
if(s->table->quick_condition_rows != s->found_reconds){
rnd_records = s->table->quick_condition_rows;
}

...
tmp += (s->records -rnd_records) * ROW_EVALUATE_COST; //CPU花费
}


// 第四部分:保存获取到的最优值放置到入口参数POSITION *pos中
pos->records_read = records;
pos->read_time = best;
pos->key = best_key;
pos->table = s;
pos->ref_depend_map = best_ref_depends_map;
pos->loosescan_key = MAX_KEY;
pos->use_join_buffer = best_uses_jbuf;


loose_sacn_opt.save_to_position(s,loose_sacn_pos);
}
]best_access_path end


// 计算查询计划的花费(每次循环得到的单表的值累计)
record_count* = position->records_read;
read_time += position->read_time;
read_time += record_count * ROW_EVALUATE_cost;


/*得到累计的值(如read_time),保存到position中,下次循环时,累加。最后一个position保存的是所有表时间花费的结果;所以最优一个能代表最优(join->best_positions)*/
position->set_prefix_costs(read_time,record_count);

// 如果存在半连接,则优化半连接
if(!join->select_lex->sj_nests.js_empty()){
advance_sj_state(join_tables,s,idx,&record_count,&read_time, &loose_scan_pos);
}else{
position->no_semijoin();
}


++idx; //确保下次调用best_access_path函数时,从本次循环使用的表s的下一个表的位置正确
}


// 最后一个代表的是最优路径
memcpy(join->best_positions,join->positions,sizeof(PoSITION)*idx);
}
]optimize_straight_join end 


[greedy_search begin
bool Optimize_table_order::greedy_search(table_map remaining_tables){
// 跳过常量表,指向第一个要连接的表
uint idx = join->const_tables; //index into 'join->best_ref'
...
do{
join->best_read = DBL_MAX;
join->best_rewcount = HA_POS_ERROR;
/*得到"idx减去常量表"个表连接的最优查询执行计划,存放到join->best_positions[idx]。另外注意,
这个函数将递归调用,采用深度优先的方式生成局部最优查询执行计划*/
if(best_extension_by_limited_search(remaining_tables,idx,record_count, read_time, search_depth)){ //第七层


DBUG_RETURN(true);
[best_extension_by_limited_search begin
bool Optimize_table_order::best_extension_by_limited_search(
table_map remaining_table,/*待连接的多个表(所有减常量)。等待连接的表的集合,最初所有的简单表都在这个集合中,随着连接的发生,连接过的表会在此集合中去除;注意这个参数本质上是一个位图,去除表相当于从其上去掉表对应位置的位图上的标记*/
uint idx,
double record_count, //当前最好的局部查询计划的元组数
double read_time, //当前最好的局部查询计划的花费
uint current_search_depth)/*搜索深度,递减*/{
for(JOIN_TAB **pos = join->best_ref + idx;*pos;pos++){
JOIN_TAB *const s = *pos; //被连接的表(循环次数增加,将连接下一个表)
const table_map real_table_bit = s->table->map; //本次循环被连接的表的位图标识


// if的4个条件不满足,则跳过本表,宣召下一个满足连接条件的表进行连接
if((remaining_tables & real_table_bit) && //本次要连接的表尚未连接到路径中
!(eq_ref_extended & real_table_bit) && /*连接表不是EQ_REF类型(如是,则因主键是唯一键的原因使得两表间的元组数是1:1的关系*/
!(remaining_tables & s->dependend) && /*本次要连接的表与尚未被连接的表没有关联(如有关联,则可能有外连接这样的依赖关系,则表间的次序需要考虑)*/
(!idx || !check_interleaving_with_nj(s))){
[check_interleaving_with_nj begin //检查嵌套连接的情况.判断一个表是否可以被连接
bool Optimize_table_order::check_interleaving_whit_nj(JOIN_TAB *tab) {//tab为准备连接的表
// 准备连接的表已经连接过了(不在ur_embedding_map)则不能再连接了。
if(cur_embedding_map & ~tab->embedding_map){
return true;
}


const TABLE_LIST *next_emb = tab->table->pos_in_table_list->embedding;
// 如果不受限制(不存在嵌套连接或外连接约束),寻找tab表之后语意上再一次存在嵌套的情况
for(;next_emb != emb_sjm_nest;next_emb = next_emb->embedding){
// 跳过连接嵌套的情况,外连接不忽略
if(!next_emb->join_cond(){
continue;
}


next_emb->nested_join->nj_counter++; //嵌套连接层次探索,再进一层
cur_embedding_map != next_emb->nested_join->nj_map; //把新探索到的嵌套层次记录到位图中
// 某一层嵌套,嵌套的表数;某一层嵌套中已经探索的表数;如果不能,则意味着这个表可以被连接
if(next_emb->nested_join->nj_total != next_emb->nested_join->nj_counter){
break;
}


// 否则,这个表已经连接过了,消除掉连接历史
cur_embedding_map &= ~next_emb->nested_join->nj_map; //记录表被加入的历史
}

return false;
}
]check_interleaving_with_nj end
best_access_path(s,remaining_tables,idx,false,record_count,position,&loose_scan_pos);


//计算表s连接到join->position[idx]中的花费:元组数、IO花费的时间
current_record_count = record_count * position->records_read;
current_read_time = read_time + position->read_time + current_record_count * ROE_evaLUATE_COST;
position->set_prefix_costs(current_read_time,current_record_count);
/*以上代码得到了当前表s和当前局部查询计划代表的临时表做连接产生的新的最优局部查询计划*/


if(has_sj){ //如果存在半连接,则调用advance_sj_state函数对半连接优化
advance_sj_state(remaining_tables,s,idx,&current_record_count,&current_read_tie,&loose_scan_pos);
}else{
position->no_semijoin();
}


// 如果本次连接生成的新路径花费更大,则调用backout_nj_state函数取消本次连接
if(current_read_time >= join->best_read){
backout_nj_state(remaining_tables,s);
continue;
}


/*启发式规则:剪枝掉一些没有优化前途的局部查询执行计划。可能会丢掉最优的查询执行计划。剪枝属于非穷举搜索范围*/
if(prune_level == 1){ //如果用户指定可做剪枝优化
// 找出较小的元组数和时间花费
if(best_record_count > current_record_count 
|| best_read_time > current_read_time 
|| (idx == join->const_tables && //'s'is the first table in the QEP
s->table == join->sort_by_table)){
if(best_record_count >= current_record_count 
&& best_read_time >= current_read_time /*TODO:what is the reasoning behind this condition?*/
&& (!(s->key_dependent & remaining_tables)
|| position->records_read<2.0)){
best_record_count = current_record_count;
best_read_time = current_read_time;
}
}else{ //否则,本次连接生成的路径花费更大,则调用backout_nj_state函数取消本次连接
backout_nj_state(remaining_tables,s);
continue;

}
} //剪枝优化结束


// 去掉已经连接的表,余下的表示下次可被连接的表
const table_map remaining_tables_after = (remaining_tables & ~real_table_bit);


/*当一个表被连接后,还有表没有被连接过且搜索深度还没有结束,则继续深度优先搜索,这样,可以深入下一层*/
if((current_search_depth>0) && remaining_tables_after){
// 启发式规则:为了避免对有所情况穷举
if(prune_level == 1 //允许对查询计划执行剪枝优化
&& position->key != NULL //通过EQ_REF或REF方式连接
&& position->records_read <= 1.0) //存在唯一键,所以连接的关系元组是1:1{
/*只为第一个满足EQ_REF的表进行eq_ref_extension_by_limited_search函数确定的连接探索*/
//eq_ref_extended之后不再为0,判断条件不再满足
/*以下代码要用除s表外的其他没有连接过的表和局部最优查询执行计划进行连接(通过递归调用本函数自己实现),探索其他的连接方式*/
if(eq_ref_extended == (table_map)0){
eq_ref_extended = real_table_bit
| eq_ref_extendsion_by_limited_search(remaining_table_after,
idx + 1, //递归调用时递增,表示用下一个表连接
current_record_count,current_read_time,
current_search_depth-1); //递减,搜索空间减少不可无限递归

if(eq_ref_extended == ~(table_map)0{
DBUG_RETURN(true); //Failed
}


bcakout_nj_state(remaining_tables,s);


/*如果递归调用使得eq_ref_extended从0变为remaining_tables,则本循环不再需要进行下去,因为本次循环之后的所有表,都满足了EQ_REF,都被前面eq_ref_extension_by_limited_search函数递归处理过了*/
if(eq_ref_extended == remaining_tables){
goto done;
}


continue;
}else{ //Skip,as described above
backout_nj_stat(remaining_tables,s);
continue;
}
} //if(prunable ...)


// 对余下的表,深度优先做连接。递归调用自己
if(best_extension_by_limited_search(remaining_tables_after, //本次连接后余下的表进行下轮连接
idx + 1, //加1表示下一个表连接到下一个位置
current_search_depth-1)) //搜索深度递减
{
DBUG_RETURN(true);
}

}else { //if((current_search_depth >1) && ...
//对过滤后得到的局部查询树(保存到join->best_positions)做花费估算
consider_plan(idx,current_record_count,current_read_time,&trace_one_table);


[consider_plan begin //考虑新生成的查询计划是否是最优的
void Optimize_table_order::consider_plan(unit idx,double record_count,double read_tie,Opt_trace_object *trace_obj){
/*启发式规则:如果有排序操作且排序表不是连接中的第一个表(常量表不计算在内),考虑把排序操作的花费加在总花费中。如果排序表是连接中的第一个表,排序操作可被消除,所以不用计算排序的花费*/
if(join->sort_by_table &&
join->sort_by_table != 
join->positions[join->const_tables].table->table){
read_tie += record_count;...
}


// 新的花费比历史上最好的花费还小,则保存新的花费到join->best_positions
const bool chosen = read_time < join->best_read;
trace_job->add("chosen",chose);
if(chosen){
memcpy((uchar*)join->best_positions,(uchar*)join->positiions,sizeof(POSITION)*(idx + 1));


join->best_read = read_time - 0.001;
join->best_rowcount = (ha_rows)record_count;
}
}

]consider_plan end
}


backout_nj_state(remaining_tables,s);
}// 结束:if((remaining_tables & real_table_bit) &&
} //结束:for(JOIN_TAB **pos = join->best_ref + idx;*pos;pos++)
...
}
]best_extension_by_limited_search end
}


// 最优的计划对应的位置
best_pos  join->best_positions[idx];
best_table = best_pos.table;


/*join->positions[idx]可能被best_extension_by_limited_search函数在递归的过程中更改,所以在本次递归结束后改为原值备用*/
join->positions[idx] = best_pos;


// 在join->best_ref 中找到best_table的位置
best_idx = idx;
JOIN_TAB *pos = join->best_ref[best_idx];
while(pos && best_table != pos){ //找位置
pos = join->best_ref[++best_idx];
}


// 把最优JOIN_TAB放到找到的位置join->best_ref[idx]上
memmove(join->best_ref + idx + 1;join->best_ref + idx,sizeof(JOIN_TAB*)*(best_idx - idx));
join->best_ref[idx] = best_table;


// 计算连接后的元组数和时间花费
//join->positions[idx]存放的是新连接的表在连接次序中应该处于的最好位置
/*这个最好的位置或者说这个位置标识的表best_table的元组数和之前的累积元组数的乘积,就是到
best_table 为止进行表连接的局部最优查询计划的元组数*/
record_count* = join->positions[idx].records_read;
// 同上,累计时间花费是加和,表示的是IO花费
read_time += join->positions[idx].read_time + record_count * ROW_EVALUATE_COST;


//计算得到最优局部路径的花费。每次循环,累计一次元组数和IO花费
// 元组数,是从初始值1累乘的结果(连接的语义做的是乘积动作)
// 花费,是从初始值0累加的结果,把每次新的连接花费加上,是IO花费
record_count* = join->positions[idx].records_read; //通过连接操作可得到的元组数
read_time += join->positions[idx].read_time //单表读数据的花费
+ record_count * ROW_EVALUATE_COST; //计算连接的cpu花费。


//从待连接的表中,去除已经连接的表
remaining_table &= ~(best_table->table->map);
--size_remain;
++idx; //指向下一个待连接的表
}while (true); //进入下一次循环,直到得到贪婪算法认为的最优查询执行计划)

}
]greedy_search end

}


// 确定子查询的优化策略(exists 和 materialization 二选一)
if(join->decide_subquery_strategy()){
DBUG_RETURN(true);

}
[decide_subquery_strategy begin //确定子查询的优化策略
bool JOIN::decide_subquery_strategy(){
switch(unit->item->substype()){
case Item_subselect::IN_SUBS:
case Item_subselect::ALL_SUBS:
case Item_subselect::ANY_SUBS:  //只支持以上3种类型的子查询,选取子查询优化策略
// All of those are children of Item_in_subselect and may use EXISTS
break;
default: //其他类型的子查询不支持选取子查询优化策略
return false;
}


// 物化不支持UNION操作
DBUG_ASSERT(chosen_method != Item_exists_subselect::EXEC_MATERIALZATION);


if((chosen_method == Item_exists_subselect::EXEC_EXISTS_OR_MAT)
&& compare_costs_of_subquery_strategies(&chosen_method)){
// 计算EXISTS strategy 和materialization二者的花费
return true;
}


switch(chosen_method){
case Item_exists_subselect::EXEC_EXISTS:
return in_pred->finalize_exists_transform(select_lex);/*子查询进化EXISTS strategy转换*/
case Item_exists_subselect::EXEC_MATERIALIZATION:
return in_pred->finalize_materialization_transform(this); //子查询进化"materialization"转换
default:
DBUG_ASSERT(false);

return true;
}
}
]decide_subquery_strategy end

// 根据choose_table_order 得到最优路径生成查询执行计划
if(join->get_best_combination()){
DBUG_RETURN(true);
}
[get_best_combination begin //生成最终的多表连接的执行顺序
bool JOIN::get_best_combination(){
// 计算临时表的数目,最多限制使用两个临时表
// 计算临时表,是因为临时表在查询树上需要占据位置
// 在优化的过程中,一些操作需要创建临时表(如分组操作,DISTINCT操作)
uint tmp_tables = (group_list ? 1:0) 
+ (select_distinct ? 1:0) 
+ (order ? 1:0)
+ (select_options & (select_big_result | option_buffer_result) ? 1:0);
if(tmp_tables > 2){
tmp_tables = 2;
}


/*使用可物化的嵌套半连接重新整理查询中表的位置
变化内容是:
1)增加临时表节点
2)变化的节点
嵌套半连接 被替换为 物化的临时表作为一个"引用"
物化的子查询包括的表 位置变换到 内标之后
*/
// 初始时,认为都是内连接
uint outer_target = 0; // 所以,外连接的表位置是0
// 内连接表位置是所有表加临时表数,从大到小按表的关系减少(向左移动)
uint inner_target = primary_tables + tmp_tables;
uint sjm_nests = 0; //嵌套的半连接的个数


// 遍历best_positions上所有表,统计半连接表的个数
for(uint tableno = 0;tableno < primary_tables;){
// 遇到需要半连接操作处理的表,sjm_nests计数
// 第一个内表位置(inner_target)左移(减少)半连接涉及的表的个数大小的长度
// 跳过"半连接涉及的表的个数大小的长度"个表,遍历后续的表
}
}
]get_best_combination end


}
]make_join_statistics end
}


//常量表提前解锁
if(const_tables && !thd->locked_tables_mode 
&& !(select_options & SELECT_NO_UNLOCK)){
mysql_unlock_some_tables(thd,ct,const_tables);
}


// 对distinct优化。如distinct存在于常量表上,去除distinct


reset_nj_counters(join_list); //重置嵌套连接信息


make_outerjoin_info(this); //为外连接填充信息

// 调用substitute_for_best_equal_field函数化简条件中冗余的等式
if(conds){
substitute_for_best_equal_field(conds,cond_equal,map2table);
}


// 调用substitute_for_best_equal_field函数化简每个表上条件中冗余的等式
if(JOIN_TAB *tab = join_tab + const_tables; tab < join_tab + tables; tab++){
if(*tab->on_expr_ref){
*tab->on_expr_ref = substitute_for_best_equal_field(*tab->on_expr_ref,tab->cond_equal,map2table);
(*tab->on_expr_ref)->update_used_tables();
}
}


// 对于每一个被物化操作驱动的表、视图,去掉其上不再被使用的索引信息
drop_unused_derived_keys();

// 为每个表设置扫描访问方式(尽量利用索引)
if(set_access_methods()){...}


// 对条件尽量求值,并下推约束条件到表中
if(make_join_select(this,select,conds)){...}




{
// 排序优化:消除排序条件中的常数(如去掉重复的排序操作)
ORDER *org_order = order;
order = ORDER_with_src(remove_const(this,order,conds,1,&simple_order,"ORDER BY"),order.src);

// 如果orderby子句后是null或常量表达式,则禁止排序
if(!order && org_order){
skip_sort_order = 1;
}
}


// 优化groupby/distinct子句(去除groupby,distinct,orderby子句),前提是不存在聚集函数
{
// 优化点有:
// 1)xxx
// 2)xxx
...
// 6)xxx
}


/* 为各种类型的表的数据获取做准备工作(如获取读取数据的标识,如何获取第一条数据和其他数据;
代码实现是通过函数指针为TABLE->status、TABLE->read_first_record等赋值完成的)*/
make_join_readinfo(
[make_join_readinfo begin //为连接的每个表构造信息
bool make_join_readinf(JOIN *join,ulonglong options,uint no_jbuf_after){
//如果指定排序或分组操作,则对join中的第一个表(非常亮表)进行排序操作
bool strted = (join->order || join->group_list);


//建立半连接重复元组消除策略(重复的元组按半连接的语义保留一个就好)
if(setup_semijoin_dupps_elimination(join,options,no_jbuf_after)){
DBUG_RETURN(TRUE);/*purecov:inspected*/
}


// 遍历除常亮表外的所有表,为每个表建立连接缓存、下推索引条件到表等
for(uint i = join->const_tables;i<join->tables;i++){
//为tab上的各成员赋值
tab->read_record.table = table;


/*对不同类型的表进行处理,主要是确认有无缓存可用,索引条件是否可以下推(如可下推,则算是一个优化点)*/
switch(tab->type){
case JT_EQ_REF;
case JT_REF_OR_NULL:
case JT_REF:
if(tab->select){
tab->select->set_quick(NULL);
}


delete tab->quick;
tab->quick = 0;
/*循环的启示位置,是从常亮表的下一个位置开始的,所以不可能有常亮表被遍历。但是如果连接中包括外连接等,则外连接中包括的常亮表是不能放到里连接的最前面的,所以可以被遍历到*/
case JT_SYSTEM:
case JT_CONST:
// 主要调用了:
//1)setup_join_buffering:为连接建立缓存
//2)push_index_cond:把索引条件下推,可快速获取单表数据


break;
case JT_ALL:
if(setup_join_buffering(tab,join,options,no_jbuf_after,&icp_other_tables_ok)){
...
}


// 动态快速扫描(MySQL支持两种算法,包括QS_RANGE,QS_DYNAMIC_RANGE)
if(tab->use_quick == QS_DYNAMIC_RANGE){
join->thd->set_status_no_good_index_used();
tab->read_first_ecord = join_init_quick_read_record;
}else{
// 如果有索引可用,使用索引
if (table->no_keyread)
{
}


//把索引条件下推,可快速获取单表数据
if(tab->select && tab->select->quick &&
tab->select->quick->index != MAX_KEY && !tab->table->key_read){
push_index_cond(tab,tab->select->quick->index,icp_other_tables_ok,&trace_refine_table);
}
}


break;
case JT_FT::
break;
default:
DEBUG_PRINT("error",("Table type %d found",tab->type));/*purecov:deadcode*/
break;/*purecov:deadcode*/
case JT_UNKNOWN:
abort();/*purecov:deadcode*/
}

//优先访问物化起源表
if(tab->table->pos_in_table_list->uses_materialization()){
tab->materialize_table = join_materialize_derived;
}


...
}
}
...


]make_join_readinfo
)



// 化简in子查询,前提是groupby和orderby子句,且顶层from子句只有一个表
if(!group_list && !order 
&& unit->item && unit->item->substype() == item_subselect::IN_SUBS 
&& primary_tables == 1 && conds 
&& !unit->is_cnion()){...}


/*如要了解"依据索引是如何读取数据"的实现方式,可从pick_table_access_method入手查各指针函数即可掌握数据获取方式*/
for(unit i = const_tables;i < tables; i++){
pick_table_access_method(&join_tab[i]);
}

// 初始化临时表
if(make_tmp_tables_info()){
dbug_return(1);
}






{//第4层
update_ref_and_keys();
get_quick_record_count();
choose_plan()
{//第5层
optimize_straight_join();
greedy_search();
{//第6层
best_extension_by_limited_search();
{//第7层
best_access_path()//第8层
}//第7层结束
}//第6层结束
}//第5层结束
}//第4层结束

}//第3层结束
JOIN::exec(){//第3层
do_select();
[JOIN::exec() begin
void JOIN::exec(){
if(prepare_result(&columns_list)){
DBUG_VOID_RETURN;
}
...
result->send_result_set_metadata(*fields,Protocol::SEND_NUM_ROWS | Protocal::SEND_EOF);
error = do_select(this); //完成执行
}
]JOIN::exec() end
}

}//第2层结束
}//第1层结束

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值