從Join的實現(xiàn)窺探MySQL迭代器
以如下left join查詢語句為范例:
select * from t1 left join t2 on t1.c=t2.a ;
以下初始化數(shù)據(jù):
1 DROP TABLE IF EXISTS `t1`;
2 CREATE TABLE `t1` (
3 `a` int DEFAULT NULL,
4 `b` varchar(20) DEFAULT NULL
5 )
6 INSERT INTO `t1` VALUES (1, 'a');
7 INSERT INTO `t1` VALUES (1, 'b');
8 INSERT INTO `t1` VALUES (4, 'a');
9 INSERT INTO `t1` VALUES (5, 'a');
10
11 DROP TABLE IF EXISTS `t2`;
12 CREATE TABLE `t2` (
13 `c` int DEFAULT NULL,
14 `d` varchar(20) DEFAULT NULL
15 )
16 INSERT INTO `t2` VALUES (9, 'i');
17 INSERT INTO `t2` VALUES (1, 'i');
18 INSERT INTO `t2` VALUES (2, 'i');
19 INSERT INTO `t2` VALUES (3, 'i');
1.處理join的yacc入口
在sys_yacc.yy?文件內(nèi)解析t1 left join t2 on t1.c=t2.a;對應(yīng)處理位置。
1 table_reference outer_join_type table_reference ON_SYM expr
2 {
3 $$= NEW_PTN PT_joined_table_on($1, @2, $2, $3, $5);
4 }
其中outer_join_type對應(yīng)。
1 outer_join_type:
2 LEFT opt_outer JOIN_SYM { $$= JTT_LEFT; }
3 | RIGHT opt_outer JOIN_SYM { $$= JTT_RIGHT; }
入?yún)⑻幚碓诤瘮?shù)T_joined_table_on內(nèi)。
2.移步到函數(shù)PT_joined_table_on
從PT_joined_table_on?聲明可知其繼承PT_joined_table?函數(shù),入?yún)⒆笥冶碣x值為PT_joined_table內(nèi)定義的tr1和tr2。
函數(shù)PT_joined_table_on?將輸入join的左右表加入context內(nèi),并調(diào)用add_join_on將on內(nèi)的條件加入右表,記錄后續(xù)數(shù)據(jù)過濾條件。
3.執(zhí)行階段函數(shù)do_command(thd)
具體對應(yīng)執(zhí)行函數(shù)int mysql_execute_command(THD *thd, bool first_level)?,語句解析以及相應(yīng)參數(shù)保存完成后,進(jìn)入函數(shù)int mysql_execute_command(THD *thd, bool first_level)?,此函數(shù)內(nèi)根據(jù)前面解析到的命令類型switch (lex->sql_command)?調(diào)用對應(yīng)的處理函數(shù),如當(dāng)前語句為例查詢命令解析為lex->sql_command = SQLCOM_SELECT?則進(jìn)入函數(shù)lex->m_sql_cmd->execute(thd)?;其對應(yīng)為sql_select.cc?內(nèi)函數(shù)bool Sql_cmd_dml::execute(THD *thd)。
4.優(yōu)化器操作,生成access_paths
sql_select.cc?內(nèi)函數(shù)bool Sql_cmd_dml::execute(THD *thd)?函數(shù)內(nèi)主要操作為函數(shù)execute_inner?,在函數(shù)execute_inner內(nèi)首先會對當(dāng)前的執(zhí)行優(yōu)化操作。
調(diào)用查詢表達(dá)式Query_expression?的優(yōu)化器unit->optimize?,此函數(shù)中會對該Query_expression?的內(nèi)的每個查詢塊query_block分別先進(jìn)行優(yōu)化操作。
查詢塊內(nèi)函數(shù)bool JOIN::optimize()?內(nèi)會將每個查詢塊優(yōu)化生成查詢執(zhí)行計劃 ,具體執(zhí)行函數(shù)為函數(shù)JOIN::create_access_paths()內(nèi)create_root_access_path_for_join()?函數(shù),以當(dāng)前查詢?yōu)槔诤瘮?shù)create_root_access_path_for_join?內(nèi)根據(jù)參數(shù)條件主要調(diào)用ConnectJoins函數(shù)。
在函數(shù)ConnectJoins?內(nèi)調(diào)用FindSubstructure判斷是join類型內(nèi)連接、外連接、半鏈接等類型。
根據(jù)FindSubstructure?返回join類型調(diào)用相應(yīng)的函數(shù)生成path,當(dāng)前查詢?yōu)槔龍?zhí)行調(diào)用CreateHashJoinAccessPath生成path。
至此查詢塊query_block?的優(yōu)化操作和path生成完成,查詢塊優(yōu)化操作完成后再執(zhí)行整體表達(dá)式Query_expression的優(yōu)化和path的生成,因為目前范例僅為一個查詢塊,所以當(dāng)前無需再做整體表達(dá)式的優(yōu)化和path生成。
5.創(chuàng)建迭代器iterator
根據(jù)上一步生成的path調(diào)用CreateIteratorFromAccessPath函數(shù)生成迭代器,用于循環(huán)操作各表數(shù)據(jù)。
在此函數(shù)內(nèi)會根據(jù)path的類型調(diào)用生成不同類型的迭代器,以目前范例為例,會調(diào)用迭代器類型為HashJoinIterator。
6.上述4、5步執(zhí)行完成后,執(zhí)行迭代器iterator
在函數(shù)execute_inner?內(nèi)執(zhí)行完成上述4、5步驟操作后主要繼續(xù)執(zhí)行unit->execute(thd)?函數(shù),其對應(yīng)執(zhí)行查詢表達(dá)式函數(shù)bool Query_expression::ExecuteIteratorQuery(THD *thd)。
函數(shù)Query_expression::ExecuteIteratorQuery?內(nèi)主要執(zhí)行m_root_iterator->Init()?,迭代器iterator初始化,當(dāng)前范例為使用HashJoinIterator?類型迭代器,因此對應(yīng)執(zhí)行迭代器函數(shù)HashJoinIterator::Init()。
執(zhí)行m_build_input->Init()?來初始右表table句柄,用于下面函數(shù)BuildHashTable()?內(nèi)讀取右表數(shù)據(jù)以便初始化返回數(shù)據(jù)存儲表hashtable?,值得注意的是BuildHashTable?函數(shù)內(nèi)會根據(jù)處理流程調(diào)用SetReadingProbeRowState設(shè)置執(zhí)行狀態(tài)用于引導(dǎo)后續(xù)迭代器iterator執(zhí)行流程。
函數(shù)內(nèi)最后調(diào)用InitProbeIterator?執(zhí)行m_probe_input->Init()初始左表table句柄用于下面函數(shù)讀取左表數(shù)據(jù)。
上面操作完成后執(zhí)行m_root_iterator->Read()?函數(shù),以當(dāng)前查詢?yōu)榉独鋵?yīng)int HashJoinIterator::Read()?函數(shù),執(zhí)行過程中根據(jù)前面SetReadingProbeRowState?設(shè)置的流程狀態(tài)再選擇對應(yīng)的操作函數(shù),以當(dāng)前范例則會循環(huán)讀取左表數(shù)據(jù),而在操作函數(shù)內(nèi)也會調(diào)用SetReadingProbeRowState?來設(shè)置迭代器iterator下一步操作,直至迭代器處理完成,其中在函數(shù)Query_expression::ExecuteIteratorQuery,每次讀取一條成功后就會調(diào)用send_data操作將結(jié)果發(fā)送至客戶端,直至所有查詢結(jié)果發(fā)送完成。