1. 程式人生 > 資料庫 >資料庫查詢優化器

資料庫查詢優化器

引言

我們知道,目前通用的資料庫查詢語言是SQL語言(Structured Query Language)。SQL語言也是一種編譯型語言,需要SQL編譯器編譯後才能執行,但它與C、C++、Java等語言不同,SQL語言是一種非過程化語言,這意味著使用SQL進行操作的時候,你只需要指定你要達到什麼目的,而無需指明要怎樣達到目的。比如要查詢EMPLOYEE的所有行,使用語句“Select * From EMPLOYEE”就行了,不需要規定該怎樣查詢這些行。

既然使用者只需要解決“做什麼”的問題,那麼,“怎麼做”的問題由誰來解決呢?這正是本文要討論的問題。

優化器(Optimizer)

優化器

解決“怎麼做”問題的工具就是“優化器”。優化器也稱查詢優化器(Query Optimizer)【注意這裡的“查詢”並不僅僅指Select操作,包括Select,Update,Delete,Insert等等在內的任何帶WHERE條件子句的SQL操作都包含查詢操作的】。它的主要工作是優化資料訪問,根據提交的SQL語句,綜合各種已有的資訊(主要是系統編目表)來產生最優的可執行的訪問方案。

優化器在整個資料庫系統中佔據著至高無上的地位,它是資料庫效能的決定因素,是所有資料庫引擎中最重要的元件。當前所有的資料庫產品中,DB2的優化器是最強大的。這一點也是大多數擁有海量資料的企業選擇DB2的主要原因。(事實上,70%以上的世界500強企業使用DB2作為主資料庫)

優化器的工作可以直觀的理解為以下4個步驟:

1.接收並驗證SQL語句的語法語義;

2.分析環境並優化滿足SQL語句的方法;

3.建立計算機可讀指令來執行優化的SQL;

4.執行指令或儲存他們以便將來執行。

這其中的第2步是本文討論的重點。至於優化器的其他內容,不介紹也不知道。

 

SQL語句執行過程

先來看看SQL語句的大致執行流程(SQL編譯器是優化器的組成部分):

簡述一下整個流程:

1.語法分析(Parse Query)

SQL語句被提交給SQL編譯器,編譯器分析該語句,檢查其語法(Parse Query:語法分析),如果存在語法錯誤,編譯器就停止處理並返回錯誤資訊;如果不存在語法問題,編譯器會將SQL語句轉換為可被優化器分析的關係代數語句,並據此建立該查詢的查詢圖模型(Query Graph Model,又稱語法樹)。

2.語義檢查(Check Semantics)

語法分析完成後,編譯器會根據查詢圖模型進行語義檢查(比如檢查語句中的資料型別是否與資料庫的表列的資料型別一致),語義檢查完成後也會將相關資訊新增到查詢圖模型,包括參考約束,表檢查約束,觸發器,和檢視資訊等。

3.查詢重寫(Rewrite Query)

如果SQL語句的語法語義都沒有問題,就可以正式進行查詢操作了。這是優化器進行查詢優化的開始階段,DB2優化器三大元件之一的查詢重寫器(Query Rewriter)就是處理這一工作的。其目的是將提交的SQL語句優化成效率更高的形式,這種優化可以是基於查詢成本的考慮,也可以是基於查詢規則的考慮。舉一個直觀的例子:

考慮下面兩條SQL語句(查詢工齡為5年的員工及其所享有的年終獎級別):

Select  EMPLOYEE.Name , WELFARE .Bonus From  EMPLOYEE , WELFARE  Where  EMPLOYEE.Seniority > 5  And  EMPLOYEE.Seniority = WELFARE  .Seniority ;

Select  EMPLOYEE.Name , WELFARE .Bonus From  EMPLOYEE , WELFARE  Where  EMPLOYEE.Seniority > 5  And  EMPLOYEE.Seniority = WELFARE  .Seniority   And  EMPLOYEE.Seniority > 5;

很顯然,兩條語句的功能相同,第二條後面的“EMPLOYEE.Seniority = 5”條件還有點多餘,那麼,那條語句的執行效率更高?

答案是第二條!因為第一條將EMPLOYEE中Seniority>5的行與WELFARE中的所有行作外連線再來找Seniority相等的行,而第二條則是將EMPLOYEE中Seniority>5的行和WELFARE中Seniority>5的行作外連線再來找Seniority相等的行。顯然,第二條語句只有更少的行參與外連線,效率自然更高。

可是,我們通常寫出的查詢語句都是第一條的形式,豈不是會影響效率?這就是查詢重寫的作用所在了,優化器的查詢重寫器能自動幫我們完成查詢語句的優化,找到更高效的查詢形式。當然了,查詢重寫並不是直接對SQL語句作上述例子那樣的優化,它操作的是由語法分析轉換過的關係代數語句,且需要根據重寫圖模型提供的資訊作出形式優化。直觀上是這麼個意思而已。

查詢重寫完成語句形式的轉換後同樣會將結果儲存在查詢圖模型中。

4.優化訪問計劃(Optimizer Access Plan)

根據查詢圖模型提供的資訊,優化器會生成許多能夠滿足查詢請求的訪問計劃(執行方案方案),然後優化器綜合系統編目表中關於表,索引,列和函式等等的統計資訊,估計每種訪問計劃的執行成本,並選擇具有最小成本的方案作為最終的訪問計劃(Acess Plan)。這個進行優化選擇的工作是由DB2優化器的另兩大元件成本評估器(Estimator)和計劃生成器(Plan Generator)完成的。需要注意的是:優化器內部有若干優化度不同的優化演算法,優化度越高(優化效果好)的演算法所需要的優化時間也是越長的。優化器會根據設定的優化級來選擇不同的優化演算法。通常資料量越大,越複雜的查詢會使用優化度更高的演算法,因為執行優化演算法所花費的時間對於優化後的查詢所帶來的縮短查詢時間的收益而言是微不足道的。這應該不難理解。

5.生成可執行程式碼(Generate Executable Code)

根據最終選定的訪問計劃生成執行程式碼,類似C語言編譯後生成可被機器識別的機器碼一樣。沒什麼好說的。

6.執行訪問計劃(Execute Plan)

執行可執行程式碼,獲取查詢結果集。更沒什麼好說的。

 

這個流程還有更加細化的步驟劃分以及根據環境的不同而增加新的步驟,不去了解。最後要說明的是Visual Explain、db2exfmt、db2expln。它們是DB2資料庫系統提供的解釋工具,使用它們能夠以GUI或者文字的形式檢視DB2優化器是如何優化查詢以及最終的訪問計劃的結果。詳細說明參見:《》

 

查詢過程(Query Processing)

以上這麼多內容都算是背景知識,到這裡才算是真正進入了正題,也就是查詢過程所關注的主要問題:訪問優化,即SQL查詢過程的第4步。

 

成本估算(Cost Estimation)

成本估算(開銷估算)是資料庫系統評價一個訪問計劃優化好壞的標準。對於每一個生成的訪問計劃,優化器都必須根據相關統計資訊(statistics)對其成本進行評估。

成本估算的主要指標是CPU時間和I/O數量。另外計算記憶體也是一個重要指標,不過這個指標的各個引數在資料庫系統初始化時已經設定完成了,優化器並不能對這種硬性引數產生什麼影響。所以可以將成本簡化為CPU成本+I/O成本。

下面是關於成本估算的結構圖:

 

圖中的Statistics(統計資訊)和Filter Factor(過濾因子)是系統進行成本估算的主要參考,後面會詳細解釋。可以看到,總成本(Total Cost)=CPU成本(CPU Cost)+I/O成本(I/O Cost)。而CPU成本(CPU Cost)=基本成本(Base Cost)+頁成本(Page Cost)+掃描成本(Scan Cost)+行成本(Row Cost)。注意:所有的成本都指的是時間上的開銷,而非硬體裝置的開銷。下面對這些成本一一進行解釋。

I/O Cost

I/O成本指從磁碟取一頁資料(一次I/O取一頁資料)所花費的時間。關於磁碟儲存的文章(《》)中已經介紹過:一次I/O操作的經驗時間是1/80秒。

CPU Base Cost

CPU基本成本指不依賴物件大小的一個值固定的CPU時間開銷。

CPU Page Cost

CPU頁成本指CPU從緩衝池中定位一個數據頁的時間開銷。

CPU Scan Cost

CPU掃描成本指對資料頁中的記錄(資料行)進行Sargable謂詞掃描所需的時間開銷。

Sargable謂詞

考慮下面的SQL查詢語句:

Select * From EMPLOYEE Where EMPNO = 100 And DEPTNO <> 010

Where條件子句後面的表示式EMPNO=100和DEPTNO<>010就是謂詞,=、<>是操作符。所有的SQL謂詞中,有些是可以使用索引的,有些則不能,例如在EMPNO和DEPTNO上分別建立了索引,EMPNO=100就可以利用EMPNO的索引優化查詢,而DEPTNO<>010就無法使用DEPTNO上的索引了,至於為什麼是這樣,理解了索引機制就很容易理解了(參看:)。

IBM就將那些使用了諸如=,>,<,in,like等操作符的可以轉化為索引操作的謂詞稱為Sargable謂詞(Sargable是一個自創詞,字典中是沒有的)。可以譯作可索引謂詞。

所以CPU Scan Cost可以理解成在資料頁中使用可索引謂詞來查詢符合條件的記錄(資料行)的時間開銷。

CPU Row Cost

CPU行成本指將資料頁中查詢到的滿足Sargable謂詞條件的記錄(資料行)複製到執行緒私有的記憶體空間以及對這些記錄使用不可索引謂詞進行進一步篩選所需的時間開銷。

例項

現在以一個例項具體看看成本估算的計算過程。

考慮查詢語句:

Select C1,C2 From T1 Where C1=100 And Length(C2) >5            (=是可索引謂詞[Sargable],但Length()>則是不可索引謂詞[Residual])

現在假設:

一次I/O開銷是10ms;

T1表佔用的資料頁數量為500頁;

記錄數為10000條;

CPU Base Cost為2ms;

CPU Page Cost為1ms/page;

CPU Scan Cost為0.1ms/record;

CPU Row Cost為0.5ms/record;

filter factor為0.25,filter factor為過濾因子,指具有相同搜尋碼的記錄的條數(本例中C1=100的記錄數)佔總記錄數的比例。則滿足C1=100條件的記錄為10000*filter factor條。

則總成本Total Cost = I/O Cost + CPU Cost = I/O Cost + Base Cost + Page Cost + Scan Cost + Row Cost

                                    =500*10 + 2 + 1*500 + 0.1*10000 + 0.5*10000*0.25

                                    =7752 ms

權重(Weight)

當然了,上面的計算只是理論上的,實際上由於硬體的不同和實際需要的不同,對於CPU開銷和I/O開銷這兩個指標的相關值並不是一定的,考慮的側重點也往往是不同的,例如,訪問計劃Plan1的CPU成本為10 CPU seconds,I/O成本為100 reads;訪問計劃Plan2CPU成本為5 CPU seconds,I/O成本為500 reads。哪一個更優呢?

資料庫系統的策略是根據情況的不同為這兩個指標各增加一個權重,比如CPU 成本的權重W1為0.3,I/O成本的權重W2為0.7,則Cost(plan)=Cost(CPU)*W1+Cost(I/O)*W2。這樣就可以進行比較了。

timeron

實際上,DB2所參照的成本指標更多且計算方法更加複雜,不僅有時間上的考慮,利用率,資源佔用量等等其他方面的因素也被納入成本估算的考量。這些不同量使用特定的演算法計算得出的值使用了一個新的計量單位timeron,timeron 是IBM發明出來的計量單位,沒有公式可用來將 timeron 的數量轉換成執行一次查詢所需的時間(以秒錶示)或者比率,數量。。。但是隻要比較timeron的值就可以比較若干個訪問計劃的優劣。

由於timeron 綜合了時間、CPU 利用率、I/O 和其它因素。這些引數值是變化的,因此執行某個查詢所需的 timeron 數量是動態的,每次執行該查詢所需的 timeron 都可能不同。

 

統計資訊(Statistics)

正如上圖表示的,一個查詢優化器想要對訪問計劃進行評估並選擇最優的訪問計劃,就必須知道評估所需的各種資訊,這些資訊包括各種表,行,索引,資料頁佔用,列值等資料庫物件的方方面面的資訊。這些資訊就被統稱為統計資訊。無資訊,無優化,只有掌握足夠的統計資訊,優化器才可能進行更準確的估算,作出更優化的選擇。統計資訊被儲存在系統編目表(System Catalog)中。

但是需要特別注意的是:統計資訊並不是自動收集(gather)的,對資料庫的某些諸如table load、index create等操作對資料庫的改變並不會被自動更新到系統編目表的統計資訊中,比如對於查詢:

Select * From EMPLOYEE Where DEPTNO = 010

如果沒有為DEPTNO建立索引,資料庫系統就只能對EMPLOYEE表進行表掃描了,而如果後來添加了索引,再進行上述查詢,系統仍然只能進行表掃描,因為系統編目表中並沒有這條索引的資訊,優化器就無法探知該索引的存在,只能使用舊的統計資訊進行優化了。

因此,為了使這些更新馬上被統計資訊收錄併為優化器所用,需要使用一條特殊的命令強制資料庫系統將表或索引的統計資訊更新到系統編目表中。DB2資料庫使用RUNSTATS命令完成這一收集操作,Oracle則使用Analyze命令實現。

需要說明的是:從DB2 V9開始擁有了自動維護的特性,其中就包括統計資訊的自動收集。資料庫系統會自動判斷哪些情況需要進行統計資訊收集工作並自動進行統計資訊的更新。無需再由DBA手動進行這一操作。Oracle從10g版開始也加入了這一特性。

 

過濾因子(Filter Factors)

過濾因子是資料庫系統中一個非常重要的概念,它是資料庫優化器進行優化的一個重要引數。過濾因子源於DB2對於查詢優化的需要。Oracle中也引入了這一概念。其作用籠統的說就是過濾。

對於一個特定的謂詞P,我們認為P的過濾因子為表中滿足謂詞P的約束條件的行數佔表中所有行數的比例。記為FF(P);

例如查詢:

Select * From EMPLOYEE Where EMPNO=101

假設EMPLOYEE表中總共有1000行,由於EMPNO是主鍵,所以滿足EMPNO=101的只有一行。因此FF(EMPNO=101)=1/1000=0.001;

再比如查詢:

Select * From EMPLOYEE Where SALARY Between 50000 And 100000

假設EMPLOYEE表中總共有1000行,工資介於50000到100000的員工有20人,則FF(SALARY Between 50000 And 100000)=20/1000=0.02;

 

過濾因子對優化器在索引的選擇上有非常重要的影響,會和統計資訊一起單獨介紹,這裡不細說。

 

解釋計劃(Explain Plan)

DB2和Oracle都提供了訪問計劃解釋工具,可以使用這些工具檢視優化器是如何進行訪問計劃的優化以及最終選擇了怎樣的訪問計劃。

DB2提供的工具有四種:db2expln命令、dynexpln命令、db2exfmt命令和Visual Explain。

db2expln命令可以用於檢視系統編目表中儲存的靜態SQL包(靜態SQL語句編譯後生成SQL包),另外也可以動態的執行SQL語句。但是無論如何,該命令只能檢視優化器最終選擇的訪問計劃的資訊。dynexpln命令作用類似db2expln,用於相容DB2早期版本產品。

db2exfmt命令則可以檢視包括統計資訊在內的優化器詳細資訊,能夠呈現優化器詳細的優化過程。

Visual Explain是以圖形化的形式呈現優化器詳細資訊,相當於db2exfmt命令的GUI呈現方式。

其中db2expln、dynexpln、db2exfmt命令支援以文字資訊方式和文字樹模型方式顯示資訊,Visual Explain支援以文字資訊和視覺化樹模型的方式顯示資訊。

關於解釋工具的詳細介紹可以參看:《》

另外,還可以使用一個特殊的Explain Plan SQL語句生成訪問計劃,該語句會對SQL語句進行編譯並將生產的訪問計劃儲存在若干解釋表中。注意:資料庫建立時並不會自動建立這些表,因為它們對於資料庫效能沒有存在意義。如果需要使用Explain Plan語句就必須先手動建立這些表才行。資料庫通常會提供建立這些表的SQL指令碼(DB2資料庫建立解釋計劃表的方式參看:《》)。DB2資料庫下該語句的格式為:

EXPLAIN PLAN [SET QUERYNO = n] [SET QUERYTAG = 'string'] FOR explainable-sql-statement   //可選引數還有很多

QUERYNO表示查詢號,用於為生成的訪問計劃提供一個編號,方便查詢生成的訪問計劃。例如:

explain plan set queryno = 1000 for "select * from employee where salary>70000"    //為一條SQL語句建立訪問計劃

select * from explain_statement where queryno = 1000                                                 //查詢建立的訪問計劃的詳細資訊

Oracle資料庫下該語句格式為:

EXPLAIN PLAN [SET STATEMENT_ID = 'text-identifier'] [INTO [schema.]tablename] FOR explainable-sql-statement

語句結構上大同小異,就不舉例了。

要說明的是,Explain plan語句只是對SQL語句進行編譯生成了訪問計劃,並沒有進行執行,不會產生輸出結果。

 

過程化級(Procedural Steps)

prcedural steps又稱procedural access steps 或 access steps。指的是查詢過程中的各種資料訪問方式,由於這些方式在效率上是逐級遞增(遞減)的,優化器會按照效率級別選擇訪問方式,所以將這些訪問方式成為過程化級(procedural steps),並不是表示在查詢過程中會依次使用這些訪問方式。不過有些查詢是需要幾種訪問方式結合使用的。為了便於理解,後面還是稱procedural steps為訪問方式吧。

典型的訪問方式只有2類,這兩類中又分若干小類:

表掃描  (Table Scan)

掃描表中的所有行來查詢所需資料行。有時候表掃描又可稱表空間掃描(Tablespace Scan),因為有的系統上的資料庫系統允許一個Extent上存在不同的表資料,用表掃描表述不準確。

索引掃描  (Index Scan)

通過掃描索引來定位所需資料行。索引掃描的情況非常複雜,又分為唯一索引掃描,非匹配索引掃描,匹配索引掃描,只掃描索引等幾種,這些方式甚至還可以繼續劃分,而且按照具體情況的不同,還有簡單索引,複合索引,多索引之分等等。另外,還有一種訪問方式稱為直接索引查詢,這種使用索引的訪問方式並不需要進行掃描工作,可以自己查詢定位,我們仍然直接將其歸為索引掃描。

當然不同的資料庫產品也會有一些其他型別的訪問方式,比如Oracle支援點陣圖索引掃描,但DB2不支援。另外,嚴格來說,訪問方式還有很多種,比如多級索引掃描,首行掃描,RID掃描等等,這些就不討論了,也不懂。

關於表掃描和索引掃描有非常多的內容,需要單獨介紹,這裡就不多說了。