MySQL 查詢優化器(二)

1.6多個查詢字段(常量條件)mysql

    多個查詢字段的查詢處理邏輯以下所示:sql

JOIN:prepare階段函數

setup_tables():同1.1測試。測試

setup_fields():同1.1測試。優化

setup_conds():同1.4測試。ui

JOIN:optimize階段spa

optimize_cond():相似1.4測試,不一樣之處在於查詢的where條件中有恆等常量,在優化過程當中會調用remove_eq_conds將1=1條件刪除。code

make_join_statistics():與1.4測試相似,因爲where條件查詢有兩個,而且其中一個條件能夠經過索引查詢。所以首先經過調用update_ref_and_keys()(sql\sql_select.cc:3967)函數,查找可使用索引的字段。orm

SQL_SELECT::test_quick_select():同1.5索引測試索引

get_key_scans_params():同1.5索引測試。

choose_plan():同1.3測試。

greedy_search():同1.3測試。

best_extension_by_limited_search():同1.5索引測試。

JOIN:exec階段

如下操做同1.3測試。

    經過測試能夠看出,對於常量等式對查詢優化器來講沒有任何意義,會在optimize_conds時將常量等式刪除。建議在查詢的where條件中不要出現對查詢沒有意義的條件,儘管查詢優化器會將沒有意義的查詢條件去除,並等式的右值條件儘量的轉化爲常量,例如將相似(a=b, b=1)類型轉化爲(a=1, b=1)。但從優化角度來看,儘量在sql語句中就避免這些優化邏輯。查詢計劃的類型爲ref,利用索引查找。

查詢student中多個普通字段(包含常量條件),執行SQL:

SELECT std_id, std_name, std_spec, std_***, std_age FROM student WHERE std_name="fff" AND std_age > 20 AND 1=1;

對應的查詢計劃以下所示:

查詢計劃跟單個查詢條件的索引查詢的執行相同,這說明增長非索引字段的查詢,主要用於從索引查詢的結果中過濾記錄。若是添加的索引不能過濾掉大多數的查詢記錄的話,普通查詢條件過濾記錄的代價也會較大。所以,創建索引的目的應該是儘量的過濾掉查詢記錄,那麼增長索引纔有效,不然索引查詢反而會增長查詢的代價。

1.7 LIMIT條件

    Limit條件查詢的邏輯處理流程如1.5普通條件查詢相似,惟一不一樣點在於JOIN::exec()處理過程當中,取結果集時,根據limit限制的值來進行過濾。所以,當查詢結果集較多的狀況下,limit條件能夠減小結果集傳遞的代價。

查詢studentlimit條件執行SQL:

SELECT std_id, std_name, std_spec, std_***, std_age FROM student WHERE std_spec = "computer" LIMIT 5;

1.8 IN條件

    In條件查詢的邏輯處理流程以下所示:

JOIN:prepare階段

setup_tables():同1.1測試。

setup_fields():同1.1測試。

setup_conds():同1.4測試。

JOIN:optimize階段

optimize_cond():同1.4測試。

make_join_statistics():與1.5索引測試相似,不一樣之處在於調用update_ref_and_keys()(sql\sql_select.cc:3967)函數時,查找查詢字段是否能夠用索引字段時,沒有找到可使用的索引。可是因爲查詢字段上有索引,所以調用get_quick_record_count()函數的實現邏輯SQL_SELECT::test_quick_select()函數構建查詢樹時,轉化爲OR的方式處理,並將查找的鍵值進行合併,造成查詢範圍。

SQL_SELECT::test_quick_select():同1.5索引測試

get_key_scans_params():同1.5索引測試。

choose_plan():同1.3測試。

greedy_search():同1.3測試。

best_extension_by_limited_search():同1.5測試。儘管查詢字段有索引,可是調用update_ref_and_keys()對查詢字段進行檢查是否可使用索引時,沒有可使用的索引。所以,不一樣於1.5的索引測試的過程,而是與普通查詢測試相同。

JOIN:exec階段

如下操做相似1.3測試。不一樣之處在於查詢的範圍經過搜索查詢樹,減少了查詢的範圍。

查詢studentin條件(索引)執行SQL:

SELECT std_id, std_name, std_spec, std_***, std_age FROM student WHERE std_name in ("bbb","ccc","ddd");

對應的查詢計劃以下所示:


從邏輯處理過程和查詢計劃能夠看出,IN條件的處理是轉化爲OR的條件來處理,這一點從查詢樹的構建能夠看出。查詢計劃類型爲range,沒有使用索引直接查詢,而是經過索引查找來構建查詢樹,減少查詢的範圍。

    IN條件的查詢字段爲普通字段時,因爲查詢字段無索引,所以,不會經過調用get_quick_record_count()函數的實現邏輯SQL_SELECT::test_quick_select()函數構建查詢樹,從而造成查詢範圍。而是直接執行JOIN::exec中進行全表掃描,經過查詢條件過濾查詢結果。

查詢studentin條件(普通字段)執行SQL:

SELECT std_id, std_name, std_spec, std_***, std_age FROM student WHERE std_spec in ("math","information");

對應的查詢計劃以下所示:


經過查詢計劃能夠看出,對沒有索引的字段使用IN操做跟普通的1.5的普通字段查詢測試相同。

    經過對IN條件的總體測試發現,當查詢的字段存在索引的狀況下,查詢優化器會利用查詢字段的索引,構建查詢樹,減少查詢的範圍,而後經過查詢優化數進行全掃描並返回查詢結果。而在查詢字段沒有索引的狀況下,查詢計劃和查詢邏輯跟普通查詢條件進行全表掃描同樣,在執行全表掃描時,經過查詢條件過濾查詢結果。

1.9 GROUP BY條件、ORDER BY條件、HAVING條件聯合

    之因此將三個條件聯合查詢,而非單獨測試,是因爲這些條件通常都是組合使用纔有效果。而且,單獨測試不會凸顯查詢條件的所有處理邏輯,不利於更好的理解。

    具體的查詢處理邏輯以下所示:

JOIN:prepare階段

setup_tables():同1.1測試。

setup_fields():同1.1測試。

setup_conds():同1.4測試。

setup_order():初始化order by列表。根據order by的字段,改變查詢列表中查詢字段的order by順序。並調用find_order_in_list()函數,檢查order by 的字段是否在select的字段中(調用find_item_in_list(),(sql\sql_base.cc:6835)),並查找order by列表中的字段是否在數據表中 (調用find_item_in_tables(),(sql\sql_base.cc:6602))。(sql\sql_select.cc:15037)(sql\sql_select.cc:14996)

setup_group():初始化group by列表,處理邏輯跟setup_order()的處理邏輯相同。調用find_order_in_list()函數,檢查group by 的字段是否在select的字段中(調用find_item_in_list(),(sql\sql_base.cc:6835)),並查找group by列表中的字段是否在數據表中 (調用find_item_in_tables(),(sql\sql_base.cc:6602))。(sql\sql_select.cc:15037)(sql\sql_select.cc:14996)

having處理:having的初始化過程沒有以具體的函數形式給出,可是處理邏輯相似group by。主要檢查having條件的字段是否在查詢列表中。因爲having條件是查詢字段的別名,所以在比較了查詢列表後,發現該字段即結束。

JOIN:optimize階段

optimize_cond():相似1.4測試,但不一樣之處在於進行優化的是having條件。

make_join_statistics():與1.3轉化爲group by後的測試相似。

choose_plan():同1.3測試。

greedy_search():同1.3測試。

best_extension_by_limited_search():同1.3測試。

calc_group_buffer():同1.3測試。

create_tmp_table():同1.3測試。

JOIN:exec階段

如下操做同1.3測試相似,區別在於對臨時表操做時,臨時結果的過濾方式不一樣。生成臨時結果的過程當中,會調用聚合函數進行處理。

    從處理邏輯來看,在JOIN::prepare階段增長了初始化group、order、having條件。因爲查詢字段沒有索引,所以須要進行全表掃描。與普通查詢不一樣的執行過程在於在臨時表操做時,臨時結果的過濾處理不一樣。

查詢studentgroup byorder byhaving聯合條件執行SQL:

SELECT std_spec, COUNT(std_spec) cnt FROM student GROUP BY std_spec HAVING cnt > 3 ORDER BY cnt;

對應的查詢計劃以下所示:


從查詢計劃來看,與1.3測試徹底一致。查詢計劃類型爲ALL,進行全表掃描。

1.10 UNION條件

    Union條件的查詢處理邏輯以下所示:

mysql_union():Union條件處理邏輯。調用st_select_lex_unit::prepare()函數(sql\sql_union.cc:1172)對Union中的每條語句進行處理。

st_select_lex_unit::prepare():逐條執行JOIN:prepare()處理邏輯,爲查詢處理作準備工做,並調用select_union::create_result_table()(sql\sql_union.cc:117)函數建立臨時表。

JOIN:prepare階段:主要處理過程同1.4測試。

st_select_lex_unit::exec():逐條執行JOIN:optimize()處理邏輯,對每條查詢語句進行優化。逐條執行JOIN:exec()處理邏輯,執行查詢操做。

JOIN:optimize階段:對於Union中的不一樣查詢字段類型的語句,處理邏輯有所不一樣。Union的第一句的處理邏輯同1.4主鍵查詢測試。Union的第二句的處理邏輯同1.5測試的索引字段處理。Union的第三句的處理邏輯同1.5測試的普通字段處理。

JOIN:exec階段:對不一樣的Union中不一樣查詢字段類型的語句,處理邏輯不一樣,並將查詢的結果存儲到臨時表中。同JOIN:optimize階段的對應關係相同。

st_select_lex_unit::init_prepare_fake_select_lex():構建一條查詢語句,獲取查詢結果。

mysql_select():同1.1測試,獲取查詢結果。

從處理邏輯來看,UNION條件的處理至關於分別對不一樣的查詢子句根據對應的查詢條件類型進行處理。處理結束後,對分別的查詢結果再次進行查詢過濾。

查詢studentunion條件執行SQL:

SELECT std_id, std_name, std_spec, std_***, std_age FROM student WHERE std_id=2012072306 UNION SELECT std_id, std_name, std_spec, std_***, std_age FROM student WHERE std_name="bbb" UNION SELECT std_id, std_name, std_spec, std_***, std_age FROM student WHERE std_spec="math";

對應的查詢計劃以下所示:


 從查詢計劃來看,查詢類型根據UNION中各個子句的查詢字段類型,生成各自的查詢計劃。最後對查詢結果,進行進一步的過濾查詢。

相關文章
相關標籤/搜索