PostgreSQL server在接收到client傳送過去的查詢之後,首先會對SQL statement進行解析,透過lexer跟parser來辨識SQL語法中的詞彙以及其語法是否有錯誤,解析完的query會以抽象語法樹的方式在系統中表現。接著rewrite rule會被套用到query tree上面,對於view的查詢也會被展開並且弄成parse tree的sub-tree。
由於SQL是宣告式的語言,並沒有指定系統應該如何去執行query,因此最終的query會被送進optimizer優化器(也有人稱為planner)裡面來生成執行計畫。首先優化器會根據query的需求以及手上的資訊(例如SELECT的欄位以及WHERE clause是否可以用index scan來做)針對每個使用到的table來計算讀取的行數、以及要如何掃描每個table。Postgres對每個table的掃描動作有一些參數跟演算法可以算出他的cost,優化器會找出其中cost最低的。
再來優化器會用動態規劃的方式來決定兩個以上table join的順序(可以理解成窮舉所有的組合)以及join的方式,同樣透過一系列的參數跟演算法來找出cost最低的執行方法。
最終生成的執行計畫,再加入一些必要資訊之後就會送到executor來執行。
PostgreSQL對於每個資料庫查詢過程中的動作都有其對應的cost,像是從儲存裝置中讀取資料。在計算cost的時候
會將每個動作乘上其對應的cost參數。Postgres提供以下幾種參數,可以讓使用者設定:
random_page_cost:隨機從儲存裝置中讀出一個page所對應的開銷
seq_page_cost:循序讀取整張table時讀取一個page所對應的開銷
cpu_index_tuple_cost:在做index scan的時候,讀取一行資料會製造一個單位的cpu_index_tuple_cost
cpu_tuple_cost可以控制CPU分析一行已經被讀取的資料時所需的開銷
cpu_operator_cost表示Postgres每次查詢執行operator跟function時的開銷
PostgreSQL預設的random_page_cost跟seq_page_cost數值是4跟1,可能是早期針對機械硬碟隨機I/O效能不佳的問題來設計,一個常見的優化是將random_page_cost適度調降到1.5之類的數值,可以讓Postgres在隨機讀取性能較佳的現代硬體上面比較多去使用index scan。
傳統的SQL Query Optimizer幾乎是窮舉了所有query的路徑,在join的關聯數量開始增加時會變得非常費時,尤其一個table有許多種不同的資料存取方法可以嘗試。使用基因演算法的方式可以讓需要大量join操作的query優化變得有效率。
先使用傳統的Optimizer來決定每個table的存取方式,接著隨機產生幾組string表示join的順序。再來使用傳統的Optimizer來得到他們的cost之後,將cost最高的順序視為”less fit”之後剔除,用其他more fit的字串選取隨機部分來產生下一代,直到產生的數量達到預設的門檻,過程中找到最好的組合即可作為query plan。