SQL Server性能調(diào)優(yōu)之淺析SQL執(zhí)行的過程
本篇文章的議題如下:
- 查詢計劃概述
- 查詢解析
- 查詢優(yōu)化器
- 查詢執(zhí)行
- 查詢計劃的重用
查詢計劃概述
很多時候,當我們在使用sql server的時候,做的事情非常簡單:輸入sql語句,然后執(zhí)行,最后獲取結(jié)果。下面,為了使得大家更加清楚的了解Sql server的內(nèi)部機制,我們就重新來審視一個sql語句的執(zhí)行。
把sql語句提到給了之后,數(shù)據(jù)庫會執(zhí)行一系列的內(nèi)部處理,我們大致的可將內(nèi)部的處理按照執(zhí)行的順序,劃分為兩個階段:
- 發(fā)生在關(guān)系引擎中的操作
- 發(fā)生在存儲引擎中的操作
在數(shù)據(jù)庫的關(guān)系引擎中,sql 的查詢語句會解析并且將解析的結(jié)果傳遞給后面的查詢優(yōu)化器,查詢優(yōu)化器負責生成執(zhí)行計劃。之后,執(zhí)行計劃(以二級制的格式)就會被傳遞到存儲引擎里面,最后返回或更新底層的數(shù)據(jù)。
數(shù)據(jù)庫的存儲引擎會進行很多的操作,例如鎖定,索引的維護,事務(wù)的處理等。
因為本系列文章主要的剖析執(zhí)行計劃,所以我們的關(guān)注點會放在關(guān)系引擎上面。
下面,我們就來稍微詳細的討論一個sql查詢語句的執(zhí)行過程。
查詢解析
正如我們剛剛提到過:當把一個sql語句提交到了數(shù)據(jù)庫以后,sql語句最先會被傳入到關(guān)系引擎中。
當sql語句達到了關(guān)系引擎之后,首先要進行的操作就是檢查sql語句的格式是否正確。這個處理過程就是我們常說的“解析”過程。解析過程的結(jié)果就是生成一個解析樹,或者稱為查詢樹。查詢樹反映了一個查詢要執(zhí)行的邏輯步驟,查詢樹的結(jié)構(gòu)類似下面圖中所示:
其實從編譯原理的角度來看,這個解析過程就是文法和詞法的解析,最后生成語法樹。
有一點需要注意的就是:如果提交的sql語句不是一個數(shù)據(jù)操作語句(數(shù)據(jù)操作語句指Select,Insert,Update語句),那么這個語句是不會被優(yōu)化的。例如,如果提交的sql語句是創(chuàng)建一個數(shù)據(jù)表,那么這個語句是不會被優(yōu)化的,而是直接執(zhí)行。
如果提交的數(shù)據(jù)操作語句,那么之前由關(guān)系引擎創(chuàng)建的解析樹就會傳遞給algebrizer組件執(zhí)行綁定過程。在這個綁定過程過程中,這個algebrizer組件就會去檢查解析樹中的表名,列名是否都關(guān)聯(lián)到了數(shù)據(jù)庫中相應的表或?qū)ο蟮囊谩?/p>
同時,algebrizer組件還負責確定解析樹中的每個節(jié)點的類型是否和數(shù)據(jù)庫中對應的是否一致。algebrizer組件以從下到上的方式開始遍歷樹,即,先從頁級節(jié)點開始,也就是列和常量。
綁定解析是一個非常重要的過程,在這個過程中還會識別出我們自己定義的一些別名。這個過程執(zhí)行完成之后,就會產(chǎn)生一個二進制的“查詢處理樹”,這個樹會被傳遞給查詢優(yōu)化器。
查詢優(yōu)化器
查詢優(yōu)化器使用查詢處理樹和相關(guān)的統(tǒng)計信息來生成一個執(zhí)行計劃。
換句話說,查詢優(yōu)化器指出了如何最好的去執(zhí)行提交的sql語句。查詢優(yōu)化器會決定是否可以采用索引來訪問數(shù)據(jù),采用那種類型的join操作會更好(例如,盡管我們有時候在sql中寫的是Left Join,可能查詢優(yōu)化器在分析之后,在保證結(jié)果一樣的前提下,采用Inner Join)。
查詢優(yōu)化器是一個基本成本分析的優(yōu)化器。這意味著它會嘗試為每個sql語句生成成本最低的執(zhí)行計劃。
另外,我們來歸對于優(yōu)化器所用到的統(tǒng)計數(shù)據(jù)進行簡要的解析。所謂的統(tǒng)計數(shù)據(jù),就是在數(shù)據(jù)庫中描述列、索引相關(guān)信息的數(shù)據(jù),即數(shù)據(jù)的數(shù)據(jù),或稱之為“元數(shù)據(jù)”。優(yōu)化器就是結(jié)合統(tǒng)計數(shù)據(jù)和查詢處理樹來進行成本的估計的。
在默認的情況下,統(tǒng)計信息是由數(shù)據(jù)庫內(nèi)部自動的進行更新的(在調(diào)優(yōu)的時候,可以手動的更新)。
需要提及的就是:表變量是沒有任何的統(tǒng)計數(shù)據(jù)的,也就是說,如果對表變量中的數(shù)據(jù)進行查詢,優(yōu)化器是不做任何的優(yōu)化的。但是臨時表是有相應的統(tǒng)計數(shù)據(jù)的。
有一點需要注意的就是:上面的成本只是“估算”而已。一些復雜的語句可能會有很多個候選的執(zhí)行計劃,在這種情況下,查詢優(yōu)化器不會分析所有的組合,而是找出一個接近理論最小值的一個執(zhí)行計劃。計劃的成本表現(xiàn)為估計完成查詢所需的時間。最低估計成本不一定是最低的資源成本。
查詢執(zhí)行
一旦執(zhí)行計劃生成之后,操作就轉(zhuǎn)入存儲引擎中,這也是查詢真正被執(zhí)行的地方,也是根據(jù)估計執(zhí)行計劃 產(chǎn)生實際執(zhí)行計劃的產(chǎn)所。
查詢計劃的重用
從之前的一些步驟可以看到:Sql Server產(chǎn)生一個實際的執(zhí)行計劃需要很多的步驟和很多的成本(執(zhí)行計劃的過度編譯往往成為一個很大的性能問題),必須盡可能的重用執(zhí)行計劃(如果后文不做特殊說明,執(zhí)行計劃就指代“實際執(zhí)行計劃”),所以,在數(shù)據(jù)庫中,一旦執(zhí)行計劃產(chǎn)生之后,就被緩存在了內(nèi)存中(稱之為計劃緩沖)。
正如之前所提到的,當優(yōu)化器產(chǎn)生了估計的執(zhí)行計劃之后,計劃就會被傳遞給存儲引擎。其實在將估計的執(zhí)行計劃傳給存儲引擎之前,查詢優(yōu)化器就去“計劃緩沖” 中查找與現(xiàn)在估計的執(zhí)行計劃對應的實際執(zhí)行計劃。如果找到了,那么,查詢優(yōu)化器將會使用執(zhí)行計劃傳進行后續(xù)操作。這樣就避免了重新生成實際的執(zhí)行計劃。
一般而言,每個查詢的執(zhí)行計劃都只保存一個,除非查詢優(yōu)化器知道采用并行執(zhí)行可以產(chǎn)生更好的性能,此時,并行查詢的執(zhí)行計劃就被緩存起來,也就是說:同一個查詢,在計劃緩沖中有兩個執(zhí)行計劃。
執(zhí)行計劃并不是永遠被保存在內(nèi)存中的。它們也是會過期的。SQL Server會基于最近最少使用的算法來移除那些不常用的執(zhí)行計劃。下面列出了執(zhí)行計劃被移除的幾個條件:
- 系統(tǒng)產(chǎn)生了內(nèi)存壓力,需要更多的內(nèi)存,此時迫使SQL Server釋放自己占用的內(nèi)存。
- 內(nèi)存中的執(zhí)行計劃的最近使用次數(shù)為0.
- 執(zhí)行計劃沒有被現(xiàn)在的數(shù)據(jù)庫連接引用。
注:熟悉.NET的朋友,可以將之與.NET的垃圾回收機制類比理解。
今天就到這里,下一篇,我們將對執(zhí)行計劃進行更多的分析!
原文鏈接:http://www.cnblogs.com/yanyangtian/archive/2011/09/01/2162188.html
【編輯推薦】