SQL性能優化

引言:  html

  之前在面試的過程當中,總有面試官問道:你作過sql性能優化嗎?對此,個人答覆是沒有。一次沒有不是本身的錯誤,兩次也不是,但若是是屢次呢?今天痛下決心,把有關sql性能優化的相關知識總結一下,以便在不久的未來,個人回答不是「沒有」,總能多多少少說一些東西。算是長進吧。說到性能優化,本人感受到有必要先了解sql語句的執行順序,由於對優化或多或少的會有些幫助。面試

 

sql語句執行順序:  sql

  sql語句和其餘相關的編程語言最大不一樣的地方應該是執行順序。對於大多數編程語言來講都是按照順序進行執行,但對於sql語句,儘管select是最開始出現,但幾乎老是最後一個執行,最開始執行的每每是from子句。每一步驟產生一個虛擬表,這些虛擬表對於調用者來講是不能用的,僅僅做用於下一步驟,而只有最後的查詢結果表才能被調用者所使用。當有步驟沒有出現時便跳過該執行步驟。下面上代碼:數據庫

(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子句中的前兩個表執行笛卡爾積(Cartesian product)(交叉聯接),生成虛擬表VT1性能優化

2)on:對VT1應用ON篩選器。只有那些使<join_condition>爲真的行才被插入VT2併發

3)outer(join):如 果指定了OUTER JOIN(相對於CROSS JOIN 或(INNER JOIN),保留表(preserved table:左外部聯接把左表標記爲保留表,右外部聯接把右表標記爲保留表,徹底外部聯接把兩個表都標記爲保留表)中未找到匹配的行將做爲外部行添加到 VT2,生成VT3.若是FROM子句包含兩個以上的表,則對上一個聯接生成的結果表和下一個表重複執行步驟1到步驟3,直處處理完全部的表爲止。編程語言

4)where:對VT3應用WHERE篩選器。只有使<where_condition>爲true的行才被插入VT4.函數

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

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

7)having:對VT6應用HAVING篩選器。只有使<having_condition>爲true的組纔會被插入VT7.

8)select:處理SELECT列表,產生VT8.

9)distinct:將重複的行從VT8中移除,產生VT9.

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

11)top:從VC10的開始處選擇指定數量或比例的行,生成表VT11,並返回調用者。

注:

步驟10,按ORDER BY子句中的列列表排序上步返回的行,返回遊標VC10.這一步是第一步也是惟一一步可使用SELECT列表中的列別名的步驟。這一步不一樣於其它步驟的 是,它不返回有效的表,而是返回一個遊標。SQL是基於集合理論的。集合不會預先對它的行排序,它只是成員的邏輯集合,成員的順序可有可無。對錶進行排序 的查詢能夠返回一個對象,包含按特定物理順序組織的行。ANSI把這種對象稱爲遊標。理解這一步是正確理解SQL的基礎。

由於這一步不返回表(而是返回遊標),使用了ORDER BY子句的查詢不能用做表表達式。表表達式包括:視圖、內聯表值函數、子查詢、派生表和共用表達式。它的結果必須返回給指望獲得物理記錄的客戶端應用程序。

  在SQL中,表表達式中不容許使用帶有ORDER BY子句的查詢,而在T—SQL中卻有一個例外(應用TOP選項)。因此要記住,不要爲表中的行假設任何特定的順序。換句話說,除非你肯定要有序行,不然不要指定ORDER BY 子句。排序是須要成本的,SQL Server須要執行有序索引掃描或使用排序運行符。

   sql語句執行時是按照從右到左的順序處理from子句中的表名,from子句中寫在最後的表也便是基礎表將被最早處理,所以在from子句中包含多個表的狀況下,選擇記錄條數最少的表做爲基礎表,在某種程度上將會極大的提升其性能。若是有3個以上的表,則選擇交叉表做爲基礎表。此處對性能優化來講至關重要。

  

執行計劃:

說完執行順序後,便討論下執行計劃:

執行計劃是數據庫根據SQL語句和相關表的統計信息做出的一個查詢方案,這個方案是由查詢優化器自動分析產生的,好比一條SQL語句若是用來從一個 10萬條記錄的表中查1條記錄,那查詢優化器會選擇「索引查找」方式,若是該表進行了歸檔,當前只剩下5000條記錄了,那查詢優化器就會改變方案,採用 「全表掃描」方式。

可見,執行計劃並非固定的,它是「個性化的」。產生一個正確的「執行計劃」有兩點很重要:

(1)    SQL語句是否清晰地告訴查詢優化器它想幹什麼?

(2)    查詢優化器獲得的數據庫統計信息是不是最新的、正確的?

 

優化檢測工具:

  基礎知識介紹完畢了,開始性能優化,可是咱們怎麼才能知道該系統中的那些sql語句應該進行性能優化,該語句是否應該進行系統優化,查看相關資料,針對sqlserver,找到sqlserver數據庫對應的有個sql server profiler,使用該工具能夠找到針對某個數據庫表來講,有什麼樣的操做行爲拉低了其性能。

打開系統主菜單--sqlserver幾---性能工具--->>sql server profiler;

而後文件--新建跟蹤--顯示跟蹤屬性窗口;

首先那個select%是個篩選監測的TextData。那個%是個通配符,他的意思就是篩選select開口的語句。固然這你本身能夠隨便定義,如update%,delete%....。

把那個排除不包含值的行也給帶上,而後肯定,運行。而後在數據庫中運行一句select。你會發現他檢測到啦。

1.查找持續時間最長的查詢

通常狀況下,最長查詢時間的查詢語句就是最影響性能的緣由存在。它不只佔用數據庫引擎大量的時間,還浪費系統資源,還影響數據庫應用系統的交互速度。再對數據用應用系統進行優化時,先找出他,對其優化,在建立跟蹤時,勾上TSQL-SQL:BatchCompleted.跟Stored Procedures-RPC:completed。這樣就能找出來這個最長時間查詢而後對其進行分析優化。

select TextData,Duration,CPU from <跟蹤的表>
where EventClass=12 -- 等於12表示BatchCompleted事件
and CPU<(0.4*Duration)  --若是cpu的佔用時間,小於執行sql語句時間的40%,說明該語句等待時間過長

2.最佔用系統資源的查詢

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

3.檢測死鎖

在訪問量,併發量都很大的數據庫中,若是設計稍不合理,就有可能形成死鎖,給系統性能帶來影響。事件包含:RPC:Starting、SQL:BatchStarting、Lock:DeadLock(死鎖事件)、Lock:DeadLockChaining(死鎖的事件序列)。

查閱SqlServer性能檢測和優化工具使用詳細

 

數據庫引擎優化顧問

和sql server profiler相對於的有個「數據庫引擎優化顧問」,也是一個與性能優化有關的工具,能夠抽時間瞭解瞭解。瞭解後再補充吧。

 

sql性能優化常見經驗:

下面總結下載網上各個大牛們認爲進行sql優化應該操做的事項:

一、模糊查詢like。

使用like進行模糊查詢時應該特別注意,這個很基本,基本上你們都知道。呵呵

select * from contact where username like ‘%yue%’

關鍵詞%yue%,因爲yue前面用到了「%」,所以該查詢必然走全表掃描,除非必要,不然不要在關鍵詞前加%。

二、where條件查詢

儘可能避免使用in,not in,having,可使用 exist 和not exist代替 in和not in。不要以字符格式聲明數字,要以數字格式聲明字符值。

三、前面提到的from子句中有多個表進行關聯查詢時

在from子句中包含多個表的狀況下,選擇記錄條數最少的表做爲基礎表,在某種程度上將會極大的提升其性能。若是有3個以上的表,則選擇交叉表做爲基礎表

四、select *查詢

儘可能不要使用

select * from tablename

取而代之的則是:

select columnname1,columnname2 from tablename

五、排序操做

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

六、索引表操做

對於此處,我的尚未弄明白,首先對於索引還不明白,那麼性能優化更談不上了。反正不少大牛都是操做索引表,須要特別注意。之後明白了再補充吧。

...

七、LEFT JOIN 和 inner join的區別,是否真的須要left join,不然選用inner join 來減小沒必要要的數據返回。

我的由於編程習慣問題,總喜歡寫left join,看來之後要用大腦思考思考了。

同時,SQL語句中鏈接多個表時, 請使用表的別名並把別名前綴於每一個Column上.這樣一來,就能夠減小解析的時間並減小那些由Column歧義引發的語法錯誤。

 

八、統一規範sql語句

編寫規範的sql語句,這一點是最重要的一點,無論對於系統仍是我的來講,都是至關的重要。

不規範的有:

很複雜的sql語句,對於編寫者本身都暈了。

大小寫隨意編寫,對於系統來講是個小麻煩。

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

 

補充:

記錄一次現場數據優化實例:

不少人有過相似的經歷,項目在本地代碼運行沒有問題,本地測試沒有問題,但系統發佈到現場報錯。最近本人遇到過相似的場景,簡單描述整個歷程。

由於是升級系統,老的文件先進行備份,現場實施替換成新文件後報錯,因而要求把新文件發回本地在代碼下運行,一番測試下顯示沒有問題。此時顯得一籌莫展了,看後臺日誌顯示超時,超時?由於涉及到http請求,若是數據量過大的話的確會超時。因而把請求過程當中用的sql語句拿到數據庫中專門執行,查詢時間大於1分鐘,此時問題就明瞭了,sql語句的問題。本地的環境和現場環境根本不可能相同,在本地不作壓力測試的狀況下,不少隱藏問題都沒有暴露出來,由此在項目開展中不會那麼順利的。

 

Top n

Sql問題,看之前相似的sql語句,發現都使用了top n,因而sql語句加上top 200,由於取前200條記錄已經能夠知足業務需求,在現場的測試環境下使用數據庫直接執行sql語句,執行時間在20s以上,若是把http請求超時時間設置的大些仍是能知足要求的,但現場實際的業務場景根本不可能讓你如此進行,這個sql僅僅是一個數據源,還有2個相似的數據源須要執行,那麼20s顯然不能知足。

 

索引

加索引,本地模擬現場的業務場景,插入了大量的測試數據,在sql的where條件查詢字段下加了索引,查詢時間進入到秒級,徹底知足項目要求。現場提供的視圖,並且視圖的廠家沒有人維護了,不可能建立其它東西的,因此雖然索引有效可是沒法使用。

 

參數

現場系統能夠經過配置參數來對業務進行調整,執行的sql語句中加入了@參數Name=@Name or @Name = '',上網通過搜索,發現參數不會對sql執行形成影響,可是若是你的where條件中的@參數正好加入了索引,那麼影響就至關顯著了。加入強制執行索引:

with(index(IX_Name)),效率有顯示提高,奈何現場的視圖已無參加維護。

 

Join

查詢數據源採用了left join聯表查詢,問題來了,主表2w多行的數據,副表也是3w多行的數據,比較奇葩的使用了兩個視圖聯表查詢,仍是那句沒有廠家維護。聯表查詢n*m,那麼減小基礎表的記錄數目能夠有效的提升效率。那麼把條件搜索放入到基礎表先進性過濾,而後再進行聯合查詢。

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

 

 

參考:

SqlServer性能檢測和優化工具使用詳細

高手詳解SQL性能優化十條經驗

優化SQL查詢:如何寫出高性能SQL語句

相關文章
相關標籤/搜索