在sql server 的性能優化過程中,tsql的語句優化是很重要的一環。當您使用各種手段找出系統最需要優化的語句后,應該如何對該語句進行優化呢?下面列出一些tsql 語句優化的常見技巧。 1. 語句的執行計劃分析 首先要對該語句的執行計劃(execution plan)進
在sql server 的性能優化過程中,tsql的語句優化是很重要的一環。當您使用各種手段找出系統最需要優化的語句后,應該如何對該語句進行優化呢?下面列出一些tsql 語句優化的常見技巧。
1. 語句的執行計劃分析
首先要對該語句的執行計劃(execution plan)進行分析,找出語句運行慢的原因。比如說,
在檢查執行計劃是否包含table scan /index scan等昂貴的操作?
對table, worktable是否進行了大量的邏輯讀?
是否使用了不合適的join類型?
并發(串行)執行計劃是否不合適 等等
舉一個的例子,
Table ‘myTable’. Scan count 1, logical reads 15877, physical reads 0, read-ahead reads 0.
SQL Server Execution Times:
CPU time = 47 ms, elapsed time = 174 ms.
Rows Executes StmtText
—— ——— ———————————————————————————————-
10 1 select EmpNo, Code, MAX(DueDate) from myTable where EmpNo = ‘21250’ group by EmpNo,Code
10 1 |–Stream Aggregate(GROUP BY:([myTable].[Code]) DEFINE:([Expr1002]=MAX([CERTIFICAT
10 1 |–Sort(ORDER BY:([myTable].[Code] ASC))
10 1 |–Table Scan(OBJECT:([SSS].[dbo].[myTable]), WHERE:([myTable]
大家看上圖,logical reads15877,很大的一個值。 執行計劃里面有table Scan,那么明顯就是一個缺少index導致表被全掃描的例子。加一個索引就會好了。
再看另外一個例子:
表 ‘myTableStatus’。掃描計數 0,邏輯讀取 4 次,物理讀取 0 次,預讀 0 次,lob 邏輯讀取 0 次,lob 物理讀取 0 次,lob 預讀 0 次
表 ‘myTable’。掃描計數 8,邏輯讀取 1408666 次,物理讀取 0 次,預讀 0 次,lob 邏輯讀取 0 次,lob 物理讀取 0 次,lob 預讀 0 次
表 ‘myTableType’。掃描計數 0,邏輯讀取 2 次,物理讀取 0 次,預讀 0 次,lob 邏輯讀取 0 次,lob 物理讀取 0 次,lob 預讀 0 次。
表 ‘Transactions’。掃描計數 0,邏輯讀取 2 次,物理讀取 0 次,預讀 0 次,lob 邏輯讀取 0 次,lob 物理讀取 0 次,lob 預讀 0 次。
Rows Executes StmtText
——————– ——————– ———————————————————————————–
2 1 SELECT * FROM vwmyTableItems WHERE 1=1 AND “myTableTypeId” = 1 AND “BranchId” = 1
2 1 |–Sort(DISTINCT ORDER BY:([j].[myTableID] DESC, [j].[UUID] ASC, [j].[ParentID] A
2 1 |–Compute Scalar(DEFINE:([Expr1009]=[Log_DB].[dbo].[FormatDate]([Log_DB].[d
2 1 |–Nested Loops(Inner Join)
1 1 |–Clustered Index Seek(OBJECT:([Log_DB].[dbo].[Transactions].[PK_
2 1 |–Nested Loops(Inner Join)
1 1 |–Clustered Index Seek(OBJECT:([Log_DB].[dbo].[myTableType].
2 1 |–Nested Loops(Inner Join, OUTER REFERENCES:([j].[myTableSta
0 0 |–Compute Scalar(DEFINE:([Expr1011]=(((substring(replic
2 1 | |–Nested Loops(Inner Join, OUTER REFERENCES:([PtnI
468971 1 | |–Index Seek(OBJECT:([Log_DB].[dbo].[myTable]
2 468971 | |–Clustered Index Seek(OBJECT:([Log_DB].[dbo]
2 2 |–Clustered Index Seek(OBJECT:([Log_DB].[dbo].[myTableS
從上面計劃看,問題是表myTable邏輯讀取 1408666 次,非常巨大。另外Nested Loop的cluster index seek 執行了468971 次. 這樣的執行計劃導致CPU 很高。 如何減少邏輯讀和減少Nested Loop里面的執行次數是關鍵。 對這類執行計劃,可以考慮改寫語句,或者嘗試不同的join type。比如,使用option(hash join) 來改變join類型,看看性能是否改善。
2. 語句的常見優化手段
分析完畢執行計劃,你知道了語句為什么慢。接下來語句的優化常見方法是如下。
表/索引 的統計信息是否最新?運行update statistics with FULLSCAN更新統計信息再看看。
對有table scan或者index scan的地方,仔細檢查是否缺少索引?運行Database tuning wizard對該語句分析下,或者手工加上索引看看。也可以查詢sys.dm_db_missing_index_details來看看系統是否大量缺少index。
join的類型是否合適,使用join hint試試試用不同的join類型。
使用index hint 試下不同的index
index是否合適,索引字段的順序是否最佳?
WHERE 語句的寫法是否不夠有效率?比如說,它是否包含了OR, ,等符號?
語句里面是否使用了自定義函數UDF?UDF常導致table scan。
語句是否導致頻繁recompile? 看看是不是temp table導致的。
語句是否返回了大量的結果集合? 返回幾萬十幾萬筆資料是有些多哦。可以使用TOP N限制結果集。
是否使用了低效率的游標?盡量使用fast_forward readonly 類型的游標比較好。
如果語句開銷很大,那么該語句是否有必要?能否減少它的執行次數?
3.簡化和重寫語句
在系統的整體性能優化里面, TSQL優化優先級并不是最高的。 下面按照對系統性能影響的重要程度依次列出優化的幾個層面:
Application
Database Design
Microsoft SQL Server
Operating System
Hardware
也就是說,程序的優化效果最明顯,接下來是的設計優化,再接下來才是TSQL的優化。硬件的優化是最后考慮比較好。一味增加內存和CPU未必能夠解決性能問題。
在程序的優化里面,如果能夠改寫數據庫訪問邏輯,改寫TSQL語句, 或者簡化TSQL語句,有時候你能夠獲得驚人的性能回報。