SQL性能分析

sql語句執(zhí)行順序:

sql語句和其他相關(guān)的編程語言最大不同的地方應(yīng)該是執(zhí)行順序。對于大多數(shù)編程語言來說都是按照順序進(jìn)行執(zhí)行,但對于sql語句,盡管select是最開始出現(xiàn),但幾乎總是最后一個執(zhí)行,最開始執(zhí)行的往往是from子句。每一步驟產(chǎn)生一個虛擬表,這些虛擬表對于調(diào)用者來說是不能用的,僅僅作用于下一步驟,而只有最后的查詢結(jié)果表才能被調(diào)用者所使用。當(dāng)有步驟沒有出現(xiàn)時便跳過該執(zhí)行步驟
(8)SELECT (9)DISTINCT (11)<Top Num> <select list>
(1)FROM [left_table]
(3)<join_type> JOIN <right_table>
(2)ON <join_condition>
(4)WHERE <where_condition>
(5)GROUP BY <group_by_list>
(6)WITH <CUBE | RollUP>
(7)HAVING <having_condition>
(10)ORDER BY <order_by_list>
邏輯查詢處理階段簡介:

1)from:對FROM子句中的前兩個表執(zhí)行笛卡爾積(Cartesian product)(交叉聯(lián)接),生成虛擬表VT1

2)on:對VT1應(yīng)用ON篩選器。只有那些使<join_condition>為真的行才被插入VT2

3)outer(join):如 果指定了OUTER JOIN(相對于CROSS JOIN 或(INNER JOIN),保留表(preserved table:左外部聯(lián)接把左表標(biāo)記為保留表,右外部聯(lián)接把右表標(biāo)記為保留表,完全外部聯(lián)接把兩個表都標(biāo)記為保留表)中未找到匹配的行將作為外部行添加到 VT2,生成VT3.如果FROM子句包含兩個以上的表,則對上一個聯(lián)接生成的結(jié)果表和下一個表重復(fù)執(zhí)行步驟1到步驟3,直到處理完所有的表為止。

4)where:對VT3應(yīng)用WHERE篩選器。只有使<where_condition>為true的行才被插入VT4.

5)group by:按GROUP BY子句中的列列表對VT4中的行分組,生成VT5.

6)cube|roolup:把超組(Suppergroups)插入VT5,生成VT6.

7)having:對VT6應(yīng)用HAVING篩選器。只有使<having_condition>為true的組才會被插入VT7.

8)select:處理SELECT列表,產(chǎn)生VT8.

9)distinct:將重復(fù)的行從VT8中移除,產(chǎn)生VT9.

10)order by:將VT9中的行按ORDER BY 子句中的列列表排序,生成游標(biāo)(VC10)

11)top:從VC10的開始處選擇指定數(shù)量或比例的行,生成表VT11,并返回調(diào)用者。

注:

步驟10,按ORDER BY子句中的列列表排序上步返回的行,返回游標(biāo)VC10.這一步是第一步也是唯一一步可以使用SELECT列表中的列別名的步驟。這一步不同于其它步驟的 是,它不返回有效的表,而是返回一個游標(biāo)。SQL是基于集合理論的。集合不會預(yù)先對它的行排序,它只是成員的邏輯集合,成員的順序無關(guān)緊要。對表進(jìn)行排序 的查詢可以返回一個對象,包含按特定物理順序組織的行。ANSI把這種對象稱為游標(biāo)。理解這一步是正確理解SQL的基礎(chǔ)。

因為這一步不返回表(而是返回游標(biāo)),使用了ORDER BY子句的查詢不能用作表表達(dá)式。表表達(dá)式包括:視圖、內(nèi)聯(lián)表值函數(shù)、子查詢、派生表和共用表達(dá)式。它的結(jié)果必須返回給期望得到物理記錄的客戶端應(yīng)用程序。

在SQL中,表表達(dá)式中不允許使用帶有ORDER BY子句的查詢,而在T—SQL中卻有一個例外(應(yīng)用TOP選項)。所以要記住,不要為表中的行假設(shè)任何特定的順序。換句話說,除非你確定要有序行,否則不要指定ORDER BY 子句。排序是需要成本的,SQL Server需要執(zhí)行有序索引掃描或使用排序運行符。

sql語句執(zhí)行時是按照從右到左的順序處理from子句中的表名,from子句中寫在最后的表也即是基礎(chǔ)表將被最先處理,因此在from子句中包含多個表的情況下,選擇記錄條數(shù)最少的表作為基礎(chǔ)表,在某種程度上將會極大的提高其性能。如果有3個以上的表,則選擇交叉表作為基礎(chǔ)表。此處對性能優(yōu)化來說相當(dāng)重要。

執(zhí)行計劃:

說完執(zhí)行順序后,便討論下執(zhí)行計劃:

執(zhí)行計劃是數(shù)據(jù)庫根據(jù)SQL語句和相關(guān)表的統(tǒng)計信息作出的一個查詢方案,這個方案是由查詢優(yōu)化器自動分析產(chǎn)生的,比如一條SQL語句如果用來從一個 10萬條記錄的表中查1條記錄,那查詢優(yōu)化器會選擇“索引查找”方式,如果該表進(jìn)行了歸檔,當(dāng)前只剩下5000條記錄了,那查詢優(yōu)化器就會改變方案,采用 “全表掃描”方式。

可見,執(zhí)行計劃并不是固定的,它是“個性化的”。產(chǎn)生一個正確的“執(zhí)行計劃”有兩點很重要:

(1) SQL語句是否清晰地告訴查詢優(yōu)化器它想干什么?

(2) 查詢優(yōu)化器得到的數(shù)據(jù)庫統(tǒng)計信息是否是最新的、正確的?

優(yōu)化檢測工具:

基礎(chǔ)知識介紹完畢了,開始性能優(yōu)化,但是我們怎么才能知道該系統(tǒng)中的那些sql語句應(yīng)該進(jìn)行性能優(yōu)化,該語句是否應(yīng)該進(jìn)行系統(tǒng)優(yōu)化,查看相關(guān)資料,針對sqlserver,找到sqlserver數(shù)據(jù)庫對應(yīng)的有個sql server profiler,使用該工具可以找到針對某個數(shù)據(jù)庫表來說,有什么樣的操作行為拉低了其性能。

打開系統(tǒng)主菜單--sqlserver幾---性能工具--->>sql server profiler;

然后文件--新建跟蹤--顯示跟蹤屬性窗口;

首先那個select%是個篩選監(jiān)測的TextData。那個%是個通配符,他的意思就是篩選select開口的語句。當(dāng)然這你自己可以隨便定義,如update%,delete%....。

把那個排除不包含值的行也給帶上,然后確定,運行。然后在數(shù)據(jù)庫中運行一句select。你會發(fā)現(xiàn)他檢測到啦。

1.查找持續(xù)時間最長的查詢

一般情況下,最長查詢時間的查詢語句就是最影響性能的原因存在。它不僅占用數(shù)據(jù)庫引擎大量的時間,還浪費系統(tǒng)資源,還影響數(shù)據(jù)庫應(yīng)用系統(tǒng)的交互速度。再對數(shù)據(jù)用應(yīng)用系統(tǒng)進(jìn)行優(yōu)化時,先找出他,對其優(yōu)化,在創(chuàng)建跟蹤時,勾上TSQL-SQL:BatchCompleted.跟Stored Procedures-RPC:completed。這樣就能找出來這個最長時間查詢?nèi)缓髮ζ溥M(jìn)行分析優(yōu)化。

select TextData,Duration,CPU from <跟蹤的表>
where EventClass=12 -- 等于12表示BatchCompleted事件
and CPU<(0.4*Duration) --如果cpu的占用時間,小于執(zhí)行sql語句時間的40%,說明該語句等待時間過長
2.最占用系統(tǒng)資源的查詢

就是占用cpu時間,跟讀寫IO的次數(shù)。建議事件包含Connect、Disconnect、ExistingConnection、SQL:BatchCompleted、RPC:completed,列包含writes,reads,cpu。

3.檢測死鎖

在訪問量,并發(fā)量都很大的數(shù)據(jù)庫中,如果設(shè)計稍不合理,就有可能造成死鎖,給系統(tǒng)性能帶來影響。事件包含:RPC:Starting、SQL:BatchStarting、Lock:DeadLock(死鎖事件)、Lock:DeadLockChaining(死鎖的事件序列)。

查閱SqlServer性能檢測和優(yōu)化工具使用詳細(xì)

數(shù)據(jù)庫引擎優(yōu)化顧問

和sql server profiler相對于的有個“數(shù)據(jù)庫引擎優(yōu)化顧問”,也是一個與性能優(yōu)化有關(guān)的工具,可以抽時間了解了解。了解后再補充吧。

sql性能優(yōu)化常見經(jīng)驗:

下面總結(jié)下載網(wǎng)上各個大牛們認(rèn)為進(jìn)行sql優(yōu)化應(yīng)該操作的事項:

1、模糊查詢like。

使用like進(jìn)行模糊查詢時應(yīng)該特別注意,這個很基本,基本上大家都知道。呵呵

select * from contact where username like ‘%yue%’
關(guān)鍵詞%yue%,由于yue前面用到了“%”,因此該查詢必然走全表掃描,除非必要,否則不要在關(guān)鍵詞前加%。

2、where條件查詢

盡量避免使用in,not in,having,可以使用 exist 和not exist代替 in和not in。不要以字符格式聲明數(shù)字,要以數(shù)字格式聲明字符值。

3、前面提到的from子句中有多個表進(jìn)行關(guān)聯(lián)查詢時

在from子句中包含多個表的情況下,選擇記錄條數(shù)最少的表作為基礎(chǔ)表,在某種程度上將會極大的提高其性能。如果有3個以上的表,則選擇交叉表作為基礎(chǔ)表

4、select *查詢

盡量不要使用

select * from tablename
取而代之的則是:

select columnname1,columnname2 from tablename
5、排序操作

避免使用耗費資源的操作,帶有DISTINCT,UNION,MINUS,INTERSECT,ORDER BY的SQL語句會啟動SQL引擎 執(zhí)行,耗費資源的排序(SORT)功能. DISTINCT需要一次排序操作, 而其他的至少需要執(zhí)行兩次排序。

6、索引表操作

索引對應(yīng)的字段應(yīng)該是頻繁查詢而不是頻繁修改

...

7、LEFT JOIN 和 inner join的區(qū)別,是否真的需要left join,否則選用inner join 來減少不必要的數(shù)據(jù)返回。

同時,SQL語句中連接多個表時, 請使用表的別名并把別名前綴于每個Column上.這樣一來,就可以減少解析的時間并減少那些由Column歧義引起的語法錯誤。

8、統(tǒng)一規(guī)范sql語句

編寫規(guī)范的sql語句,這一點是最重要的一點,不管對于系統(tǒng)還是個人來說,都是相當(dāng)?shù)闹匾?/p>

不規(guī)范的有:

很復(fù)雜的sql語句,對于編寫者自己都暈了。

大小寫隨意編寫,對于系統(tǒng)來說是個小麻煩。

肯定還有,就是平時多注意就ok了。

補充:

記錄一次現(xiàn)場數(shù)據(jù)優(yōu)化實例:

很多人有過類似的經(jīng)歷,項目在本地代碼運行沒有問題,本地測試沒有問題,但系統(tǒng)發(fā)布到現(xiàn)場報錯。最近本人遇到過類似的場景,簡單描述整個歷程。

因為是升級系統(tǒng),老的文件先進(jìn)行備份,現(xiàn)場實施替換成新文件后報錯,于是要求把新文件發(fā)回本地在代碼下運行,一番測試下顯示沒有問題。此時顯得束手無策了,看后臺日志顯示超時,超時?因為涉及到http請求,如果數(shù)據(jù)量過大的話的確會超時。于是把請求過程中用的sql語句拿到數(shù)據(jù)庫中專門執(zhí)行,查詢時間大于1分鐘,此時問題就明了了,sql語句的問題。本地的環(huán)境和現(xiàn)場環(huán)境根本不可能相同,在本地不做壓力測試的情況下,很多隱藏問題都沒有暴露出來,由此在項目開展中不會那么順利的。

Top n

Sql問題,看以前類似的sql語句,發(fā)現(xiàn)都使用了top n,于是sql語句加上top 200,因為取前200條記錄已經(jīng)可以滿足業(yè)務(wù)需求,在現(xiàn)場的測試環(huán)境下使用數(shù)據(jù)庫直接執(zhí)行sql語句,執(zhí)行時間在20s以上,如果把http請求超時時間設(shè)置的大些還是能滿足要求的,但現(xiàn)場實際的業(yè)務(wù)場景根本不可能讓你如此進(jìn)行,這個sql僅僅是一個數(shù)據(jù)源,還有2個類似的數(shù)據(jù)源需要執(zhí)行,那么20s顯然不能滿足。

索引

加索引,本地模擬現(xiàn)場的業(yè)務(wù)場景,插入了大量的測試數(shù)據(jù),在sql的where條件查詢字段下加了索引,查詢時間進(jìn)入到秒級,完全滿足項目要求。現(xiàn)場提供的視圖,而且視圖的廠家沒有人維護(hù)了,不可能創(chuàng)建其它東西的,所以雖然索引有效但是無法使用。

參數(shù)

現(xiàn)場系統(tǒng)可以通過配置參數(shù)來對業(yè)務(wù)進(jìn)行調(diào)整,執(zhí)行的sql語句中加入了@參數(shù)Name=@Name or @Name = '',上網(wǎng)經(jīng)過搜索,發(fā)現(xiàn)參數(shù)不會對sql執(zhí)行造成影響,但是如果你的where條件中的@參數(shù)正好加入了索引,那么影響就相當(dāng)顯著了。加入強制執(zhí)行索引:

with(index(IX_Name)),效率有顯示提升,奈何現(xiàn)場的視圖已無參加維護(hù)。

Join

查詢數(shù)據(jù)源采用了left join聯(lián)表查詢,問題來了,主表2w多行的數(shù)據(jù),副表也是3w多行的數(shù)據(jù),比較奇葩的使用了兩個視圖聯(lián)表查詢,還是那句沒有廠家維護(hù)。聯(lián)表查詢n*m,那么減少基礎(chǔ)表的記錄數(shù)目可以有效的提高效率。那么把條件搜索放入到基礎(chǔ)表先進(jìn)性過濾,然后再進(jìn)行聯(lián)合查詢。

select top 500 * from

(select * from [dbo].[table1] where (ss between @a1 and @a2)) a

LEFT JOIN dbo.[table2] ON a.m = dbo.[table2].n

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容