從Join的實現窺探MySQL迭代器
以如下left join查詢語句為范例:
select * from t1 left join t2 on t1.c=t2.a ;
以下初始化數據:
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?文件內解析t1 left join t2 on t1.c=t2.a;對應處理位置。
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對應。
1 outer_join_type:
2 LEFT opt_outer JOIN_SYM { $$= JTT_LEFT; }
3 | RIGHT opt_outer JOIN_SYM { $$= JTT_RIGHT; }
入參處理在函數T_joined_table_on內。
2.移步到函數PT_joined_table_on
從PT_joined_table_on?聲明可知其繼承PT_joined_table?函數,入參左右表賦值為PT_joined_table內定義的tr1和tr2。
函數PT_joined_table_on?將輸入join的左右表加入context內,并調用add_join_on將on內的條件加入右表,記錄后續數據過濾條件。
3.執行階段函數do_command(thd)
具體對應執行函數int mysql_execute_command(THD *thd, bool first_level)?,語句解析以及相應參數保存完成后,進入函數int mysql_execute_command(THD *thd, bool first_level)?,此函數內根據前面解析到的命令類型switch (lex->sql_command)?調用對應的處理函數,如當前語句為例查詢命令解析為lex->sql_command = SQLCOM_SELECT?則進入函數lex->m_sql_cmd->execute(thd)?;其對應為sql_select.cc?內函數bool Sql_cmd_dml::execute(THD *thd)。
4.優化器操作,生成access_paths
sql_select.cc?內函數bool Sql_cmd_dml::execute(THD *thd)?函數內主要操作為函數execute_inner?,在函數execute_inner內首先會對當前的執行優化操作。
調用查詢表達式Query_expression?的優化器unit->optimize?,此函數中會對該Query_expression?的內的每個查詢塊query_block分別先進行優化操作。
查詢塊內函數bool JOIN::optimize()?內會將每個查詢塊優化生成查詢執行計劃 ,具體執行函數為函數JOIN::create_access_paths()內create_root_access_path_for_join()?函數,以當前查詢為例在函數create_root_access_path_for_join?內根據參數條件主要調用ConnectJoins函數。
在函數ConnectJoins?內調用FindSubstructure判斷是join類型內連接、外連接、半鏈接等類型。
根據FindSubstructure?返回join類型調用相應的函數生成path,當前查詢為例執行調用CreateHashJoinAccessPath生成path。
至此查詢塊query_block?的優化操作和path生成完成,查詢塊優化操作完成后再執行整體表達式Query_expression的優化和path的生成,因為目前范例僅為一個查詢塊,所以當前無需再做整體表達式的優化和path生成。
5.創建迭代器iterator
根據上一步生成的path調用CreateIteratorFromAccessPath函數生成迭代器,用于循環操作各表數據。
在此函數內會根據path的類型調用生成不同類型的迭代器,以目前范例為例,會調用迭代器類型為HashJoinIterator。
6.上述4、5步執行完成后,執行迭代器iterator
在函數execute_inner?內執行完成上述4、5步驟操作后主要繼續執行unit->execute(thd)?函數,其對應執行查詢表達式函數bool Query_expression::ExecuteIteratorQuery(THD *thd)。
函數Query_expression::ExecuteIteratorQuery?內主要執行m_root_iterator->Init()?,迭代器iterator初始化,當前范例為使用HashJoinIterator?類型迭代器,因此對應執行迭代器函數HashJoinIterator::Init()。
執行m_build_input->Init()?來初始右表table句柄,用于下面函數BuildHashTable()?內讀取右表數據以便初始化返回數據存儲表hashtable?,值得注意的是BuildHashTable?函數內會根據處理流程調用SetReadingProbeRowState設置執行狀態用于引導后續迭代器iterator執行流程。
函數內最后調用InitProbeIterator?執行m_probe_input->Init()初始左表table句柄用于下面函數讀取左表數據。
上面操作完成后執行m_root_iterator->Read()?函數,以當前查詢為范例其對應int HashJoinIterator::Read()?函數,執行過程中根據前面SetReadingProbeRowState?設置的流程狀態再選擇對應的操作函數,以當前范例則會循環讀取左表數據,而在操作函數內也會調用SetReadingProbeRowState?來設置迭代器iterator下一步操作,直至迭代器處理完成,其中在函數Query_expression::ExecuteIteratorQuery,每次讀取一條成功后就會調用send_data操作將結果發送至客戶端,直至所有查詢結果發送完成。























