?
本文檔使用 PHP中文網(wǎng)手冊(cè) 發(fā)布
規(guī)劃器/優(yōu)化器(planner/optimizer)的任務(wù)是創(chuàng)建一個(gè)優(yōu)化了執(zhí)行規(guī)劃。 一個(gè)特定的 SQL 查詢(因此也就是一個(gè)查詢樹)實(shí)際上可以以多種不同的方式執(zhí)行, 每種都生成相同的結(jié)果集。如果可能,查詢優(yōu)化器將檢查每個(gè)可能的執(zhí)行規(guī)劃, 最終選擇認(rèn)為運(yùn)行最快的執(zhí)行計(jì)劃。
Note: 有些情況下,檢查一個(gè)查詢所有可能的執(zhí)行方式會(huì)花去很多時(shí)間和內(nèi)存空間。 特別是在正在執(zhí)行的查詢涉及大量連接操作的時(shí)候。 為了在合理的時(shí)間里判斷一個(gè)合理的(而不是優(yōu)化的)查詢計(jì)劃。 PostgreSQL 使用基因查詢優(yōu)化器(Genetic Query Optimizer)。 (seeChapter 50) when the number of joins exceeds a threshold (seegeqo_threshold).
規(guī)劃器的搜索過(guò)程實(shí)際上是與叫做paths的數(shù)據(jù)結(jié)構(gòu)一起結(jié)合運(yùn)轉(zhuǎn)的, 這個(gè)數(shù)據(jù)結(jié)構(gòu)是一個(gè)很簡(jiǎn)單的規(guī)劃的精簡(jiǎn)版本,它只包括規(guī)劃器用來(lái)決策所必須的信息。 在找到最經(jīng)濟(jì)的路徑之后,就制作一個(gè)完整的規(guī)劃樹(plan tree)傳遞給執(zhí)行器。 它有足夠的詳細(xì)信息,代表著需要執(zhí)行的計(jì)劃,執(zhí)行器可以讀懂并運(yùn)行之。 在本章剩余部分,將忽略路徑和規(guī)劃之間的區(qū)別。
規(guī)劃器/優(yōu)化器通過(guò)為掃描查詢里出現(xiàn)的每個(gè)關(guān)系(表)生成規(guī)劃, 可能的規(guī)劃是由每個(gè)關(guān)系上有哪些可用的索引決定的。 對(duì)一個(gè)關(guān)系總是可以進(jìn)行一次順序查找,所以總是會(huì)創(chuàng)建只使用順序查找的規(guī)劃。 假設(shè)一個(gè)關(guān)系上定義著一個(gè)索引(例如 B-tree 索引), 并且一條查詢包含約束relation.attribute OPR constant。 如果relation.attribute碰巧匹配 B-tree 索引的關(guān)鍵字 并且OPR又是列出在索引的操作符類中的操作符中的一個(gè), 那么將會(huì)創(chuàng)建另一個(gè)使用 B-tree 索引掃描該關(guān)系的規(guī)劃。 如果還有別的索引,而且查詢里面的約束又和那個(gè)索引的關(guān)鍵字匹配,則還會(huì)生成更多的規(guī)劃。
如果查詢要求鏈接兩個(gè)或兩個(gè)以上的關(guān)系,在掃描單一的關(guān)系時(shí),所有可行的計(jì)劃被找到后,鏈接關(guān)系的計(jì)劃才會(huì)被考慮。有三種可能的連接策略:
嵌套循環(huán)連接(nested loop join): 對(duì)左邊的關(guān)系里面找到的每條行都對(duì)右邊關(guān)系進(jìn)行一次掃描。 這個(gè)策略容易實(shí)現(xiàn),但是可能會(huì)很耗費(fèi)時(shí)間。 但是,如果右邊的關(guān)系可以用索引掃描,那么這個(gè)可能就是個(gè)好策略。 可以用來(lái)自左邊關(guān)系的當(dāng)前行的數(shù)值為鍵字進(jìn)行對(duì)右邊關(guān)系的索引掃描。
融合排序連接(merge join): 在連接開始之前,每個(gè)關(guān)系都對(duì)連接字段進(jìn)行排序。 然后對(duì)兩個(gè)關(guān)系并發(fā)掃描,匹配的行就組合起來(lái)形成連接行。 這種聯(lián)合更有吸引力,因?yàn)槊總€(gè)關(guān)系都只用掃描一次。 要求的排序步驟可以通過(guò)明確的排序步驟, 或者是使用連接鍵字上的索引按照恰當(dāng)?shù)捻樞驋呙桕P(guān)系。
Hash 連接hash join: 首先掃描右邊的關(guān)系,并用連接的字段作為散列鍵字加載進(jìn)入一個(gè) Hash 表, 然后掃描左邊的關(guān)系,并將找到的每行用作散列鍵字來(lái)以定位表里匹配的行。
如果查詢里的關(guān)系多于兩個(gè),最后的結(jié)果必須通過(guò)一個(gè)連接步驟樹建立,每個(gè)步驟有兩個(gè)輸入。 規(guī)劃器檢查不同的連接順序可能,找出開銷最小的。
如果查詢使用了比geqo_threshold少的關(guān)系,為了找到最好的接入序列,near-exhaustive查找被運(yùn)行。 策劃者優(yōu)先考慮接入任意兩個(gè)關(guān)系之間,那些在WHEREqualification里存在一個(gè)相應(yīng)的加入條款(例如: 存在像where rel1.attr1=rel2.attr2這樣的關(guān)系)。只有在沒有別的選擇的時(shí)候考慮加入對(duì)時(shí)沒有join字句, 也就是,一個(gè)特別的關(guān)系對(duì)另外的關(guān)系沒有可用的join子句。策劃者會(huì)為每一個(gè)加入對(duì)想到所有可能的計(jì)劃,選擇計(jì)劃的標(biāo)準(zhǔn)之一是(估計(jì)是)選擇最便宜的。
當(dāng)geqo_threshold被超過(guò),啟發(fā)式?jīng)Q定加入序列的考慮,就像在 Chapter 50里的描述。否則進(jìn)程是一樣的。
完成的查詢樹由對(duì)基礎(chǔ)關(guān)系的順序或者索引掃描組成, 并根據(jù)需要加上嵌套循環(huán)、融合、以及 Hash 連接節(jié)點(diǎn),加上任何需要的輔助步驟, 比如排序節(jié)點(diǎn)或者聚集函數(shù)計(jì)算節(jié)點(diǎn)等。 大多數(shù)這些規(guī)劃節(jié)點(diǎn)類型都有額外的做選擇(selection) (拋棄那些不符合指定布爾條件的行)和投影(projection) (基于給出的字段數(shù)值計(jì)算一個(gè)派生出的字段集,也就是在需要時(shí)計(jì)算標(biāo)量表達(dá)式)。 規(guī)劃器的一個(gè)責(zé)任是從WHERE子句中 附加選擇條件以及為規(guī)劃樹最合適的節(jié)點(diǎn)計(jì)算所需要的輸出表達(dá)式。