4 .3 .4 常見高CPU利用率的緣由

4 .3 .4 常見高CPU利用率的緣由
存在髙CPU利用率的問題類型有不少種,可是咱們能夠關注一些常見類型,至於其餘
極端類型暫時不包含。如下即是高CPU利用率的常見類型:
□缺失索引(Missing Index)
□統計信息過期
□ 非 SARG查詢
□ 隱式 轉 換 (Implicit conversions □ 參數嗅探(Parameter sniffing)
□非參數化Ad-hoc査詢 □非必要的並行查詢
下面分別介紹一下。
1 . 缺失索引
缺失索引是最多見的引發髙CPU和 I/O利用的緣由之一,當沒有合適的索引用於支
持查詢時,通常只能經過大面積掃描來獲取所需的信息。一方面,這種掃描會形成SQL
Server須要處理不少非必要的數據;另一方面,因爲須要加載不少非必要的數據到內存, 所以會引發內存壓力,致使計劃緩存被移除,更嚴重的是引發SQL Server必須從新編譯、 優化查詢,編譯和優化也是高CPU開銷操做。下面來看個例子,執行以下語句。
USE AdventureWorks2008R2 GOnode

SET STATISTICS TIME ON;
SET STATISTICS IO ON;
SELECT per.FirstName , per.LastName , p.Name , p .ProductNumber , OrderDate , LineTotal • soh.TotalDue FROM Sales.SalesOrderHeader AS soh INNER JOIN Sales.SalesOrderDetail sod ON soh.SalesOrderlD = sod.SalesOrderlD INNER JOIN Production.Product AS p ON sod.ProductlD = p.ProductID INNER JOIN Sales.Customer AS c ON soh.CustomerlD = c.CustomerlD INNER JOIN Person.Person AS per ON c.PersonlD = per.BusinessEntitylD WHERE LineTotal > 25000
SET STATISTICS IO OFF;
SET STATISTICS TIME OFF;
得 到 下 面 的 信 息 ,
SQL Server執行時間:
C P U 時間= 0 毫秒,佔用時間= 0 毫秒。
SQL Server執行時間:
C P U 時間= 6 2 毫秒,佔用時間= 9 7 毫秒。
SQL Server執行時間:
C P U 時間= 0 毫秒,佔用時間= 0 毫秒。
能夠看到,因爲缺乏索引,CPU須要花費不少時間去獲取沒必要要的數據。咱們能夠創
建一個索引,而後再看看結果。
CREATE NONCLUSTERED INDEX idx_SalesOrderDetail_LineTotal ON Sales.SalesOrderDetail (LineTotal)
索 引 後 結 果 如 下 ,
SQL Server執行時間:
CPU時間= 0 毫秒,佔用時間= 0 毫秒。
SQL Server執行時間:
CPU時間= 0 毫秒,佔用時間= 1 毫秒。
SQL Server執行時間:
C P U 時間= 0 毫秒,佔用時間= 0 毫秒。
有了索引,佔用時間幾乎爲0 , 可見效果很明顯。在本人的工做經歷中,缺失索引是最
常見的CPU消耗緣由。數據庫

2. 統計信息過期
SQL Server優化器藉助統計信息來預估查詢狀況,若是統計信息過期、不許 確 ,會導 致優化器產生不合適的執行計劃,好比表中只有幾萬數據,而統計信息顯示有幾億,這時
候優化器可能會選擇hash鏈接,這將加大各方面的資源開銷。對於這類問題,能夠在經過 執行計劃來查看,若是一個查詢,你明知道它返回和處理的結果集都很小,而優化器卻選
擇了 hash鏈接,那麼這時就能夠檢查一下圖形化執行計劃中是否有黃色歎號,或者用文本 化執行計劃看看預估和實際行數的差別是否很大。若是是,可使用UPDATE STATISTICS
語句更新統計信息,同時檢查爲何統計信息過期。
3. 非 SARG查詢
SARG是 Search Argument的縮寫。簡單來講,若是一個謂詞(特別是WHERE條件
中)能用到索引查找操做,就能夠理解爲符合SARG。可是若是在WHERE條件所用到的
列中使用了標量函數(YEAR、UPPER等),或者使用了 LIKE •%%’這類的查詢,就稱爲非
SARG査詢,會致使索引無效。這些非SARG的寫法使得SQL Server只能進行表或者索引 掃描,結果相似於缺失索引,因此一樣會引發CPU高利用。下面是典型的非SARG查詢:
SELECT soh.SalesOrderlD , OrderDate , DueDate , ShipDate , Status , SubTotal ,
TaxAmt ,
Freight , TotalDue FROM Sales.SalesOrderheader AS soh INNER JOIN Sales.SalesOrderDetail AS sod ON soh.SalesOrderlD = sod.SalesOrderlD WHERE CONVERT (date, sod.ModifiedDate) = *07/01/2005'
查看上面查詢的執行計劃能夠看出,這裏進行的是彙集索引掃描,如圖4-3所示。但
原本是能夠進行彙集索引査找的。
面對這類問題時,能夠對語句進行改寫。
SELECT soh.SalesOrderlD , OrderDate , DueDate , ShipDate , Status , SubTotal ,
TaxAmt ,
Freight , TotalDue FROM S a l e s . S a le s O r d e r h e a d e r AS so h INNER JOIN S a le s . S a le sO rd e rD e ta il AS sod ON so h .S alesO rd erlD = so d .S alesO rd erlD WHERE s o d . M odifiedD ate >= * 2 0 0 5 -0 7 -0 1 0 0 :0 0 :0 0 .0 0 0 * AND s o d . M odifiedD ate < * 2 0 0 5 -0 7 -0 2 0 0 :0 0 :0 0 .0 0 0 *緩存

另外,常見的非SARG查詢還包括對where條件中的列使用一些如UPPER/LTRIM/ ISNULL之類的標量函數。對於這類狀況,絕大部分狀況下經過改寫查詢都能解決。函數

4 . 隱式轉換
隱式轉換(執行計劃中會出現Implicit conversions)指一個查詢的FROM/WHERE子句
中,用於關聯或判斷的列之間數據類型不相等,致使優化器須要根據數據類型的優先級高
低進行類型轉換而後再優化、執行。因爲SQL Server沒法匹配不一樣類型的數據,因此須要 先將它們轉換成相同類型再進行匹配。這個步驟發生在查詢執行過程當中。可是若是出現這
種狀況,會致使查詢轉變成非SARG查詢,從而出現相似上面介紹的問題。好比下面這個優化

查詢:
SELECT p .FirstName , р . LastName , с. AccountNumber FROM Sales.Customer AS c INNER JOIN Person.Person AS p ON c.PersonlD = p.BusinessEntitylD
WHERE AccountNumber = N*AW00029594'執行上面的語句,並查看實際執行計劃中
的圖標屬性,如圖4-5所示。spa

線程

在圖4-5中,加框部分就是查詢須要把varchar類型隱式轉換成nvarchar類型。解決這 類問題最好的方法是在設計過程當中就先考慮數據類型,而且確保在where條件中變量、參 數、常量等都和數據列的類型一致。若是沒法作到,能夠考慮在傳人where條件以前先進 行屋式數據類型轉換。
5 . 參數嗅探
參數嗅探( Parameter sniffing)是 SQL Server建立針對存儲過程、函數或者參數化查
詢的執行計劃時,根據傳人的參數進行預估並生成(或重用)執行計劃的一個功能。一般來
說,參數嗅探方式是比較好的功能,由於能夠重用計劃緩存。可是有些時候,針對一個查
詢的第一次傳參已經產生了一個執行計劃,當後續傳參時,因爲存在對應參數的數據分佈
等問題,致使原有的執行計劃沒法髙效響應請求,這時候就會出現參數嗅探問題。參數嗅
探僅出如今執行計劃的編譯或者重編譯過程當中。設計

來看看下面這個例子:
—建立存儲過程
CREATE PROCEDURE user_GetCustomerShipDates
(
@ShipDateStart DATETIME , @ShipDateEnd DATETIME
)
AS
SELECT CustomerlD , SalesOrderNumber FROM Sales.SalesOrderHeader WHERE ShipDate BETWEEN @ShipDateStart AND @ShipDateEnd
GO
--建立非彙集索引,演示完畢後請刪除
CREATE NONCLUSTERED INDEX IDX_ShipDate_ASC ON Sales.SalesOrderHeader (ShipDate)
GO
— 清空緩存
DBCC FREEPROCCACHE
EXEC user_GetCustomerShipDates * 2005/07/08 *, *2008/01/01'
EXEC user_GetCustomerShipDates * 2005/07/10', 12005/07/20 *3d

 

從 圖 4-6能夠看到,雖然在ShipDate上有索引,但仍是進行的彙集索引掃描。這是因 爲在第一個存儲過程的參數中,查詢條件的時間範圍幾乎包括了全表的全部時間,另外由
於非彙集索引沒有覆蓋查詢(在第6 章介紹),所以使用了彙集索引掃描操做。而第二個存
儲過程仍然會沿用上面的執行計劃,可是實際上它只須要查詢十天的數據,按理是不該該
存在掃描,可它仍是進行了彙集索引掃描。如今把上面的存儲過程順序調換一下,注意先
清空計劃緩存。
DBCC FREEPROCCACHE -清空計劃緩存,避免原有執行計劃對本例形成影響
EXEC user_GetCustomerShipDates '2005/07/10', ,2005/07/20'
EXEC user_GetCustomerShipDates '2005/07/08', '2008/01/01'blog

 

從圖4-7能夠看到,兩個查詢都使用了索引查找。若是打開SET STATISTICS IO/TIME
這兩個配置,而後對比上面兩次查詢的CPU時間,會發現前者第一個查詢的CPU時間遠
大於第二個,然後者的第二個時間遠大於第一個。這是由於對於範圍比較大的那個參數區
間,CPU須要處理更多的數據。
對於參數嗅探問題,可使用部分重編譯、編譯提示(OPTIMIZE FOR)等功能來避
免,更多的優化應該考慮數據和研究數據分佈問題。


6 . 非參數化Ad-hoc查詢
Ad-hoc稱爲即席查詢,能夠理解爲沒有使用存儲過程、SP_ExeCUteSql或者其餘方式強 制預約義SQL語句。這類查詢會致使SQL Server每次都要檢查是否有計劃緩存可用於徹底 匹配這些語句。在這類語句中,即便只有參數不一樣,都會致使CPU分別進行編譯和優化。
而這將致使CPU的浪費(浪費在對原本能夠重用的執行計劃上),也會由於對Ad-hoc查詢 分別存放執行計劃(可能只會用一次)致使計劃緩存空間的浪費。可使用下面的計數器來
進行監控,看看是否存在這樣的浪費。
□ SQL Server: SQL Statistics: SQL Compilations/Sec □ SQL Server: SQL Statistics: Auto-Param Attempts/Sec □ SQL Server: SQL Statistics: Failed Auto-Param/Sec 若是是非參數化的A d-hoc,即不帶參數的A d-hoc,好比select * from tb where id =xxx
這類查詢引發了問題,在 SQL Server 2008中,可使用圖4-8所 示 的 「高級」選項來對其 進行優化。
或者在數據庫層面強制參數化。
ALTER DATABASE AdventureWorks SET PARAMETERIZATION FORCED

 

7 . 非必要的並行查詢
並行操做會把一個査詢分開到多個線程中執行,而後再合併到一塊兒返回結果。當一個
査詢的開銷超過cost threshold for parallelism這個閾值時(默認爲5 秒),就會檢查是否有可
用 的 CPU用於支持並行操做,其中,並行度取決於max degree of parallelism的值。 可是 對於OLTP系統,並行操做每每是非必需的,過多的並行執行會加劇CPU的負擔。能夠用
下面語句來檢查是否存在並行操做的執行計劃。SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;WITH XMLNAMESPACES(DEFAULT •http://schemas.microsoft.com/SQL Server/2004/07/showplan*) SELECT query_plan AS CompleteQueryPlan , n.value(•(@StatementText)[1]•, *VARCHAR(4000)*) AS StatementText , n.value(1 (QStatementOptmLevel) [1]*, ’VARCHAR(25) 1) AS StatementOptimizationLevel , n.value(*(QStatementSubTreeCost)[1]、 *VARCHAR(128)*) AS StatementSubTreeCost , n.query(•.') AS ParallelSubTreeXML , ecp.usecounts f ecp.size_in_bytes FROM sys.dm_exec_cached_plans AS ecp CROSS APPLY sys.dm_exec_query_plan(plan_handle) AS eqp CROSS APPLY query_plan.nodes (•/ShowPlanXML/BatchSequence/Batch/Statements/StmtSimple') AS qn ( n ) WHERE n.query(*.*).exist(*//RelOp[@PhysicalOp=M Parallelism」 ]') = 1對於存在並行操做的査詢,不建議立刻下降並行度,應該優化查詢,使其儘量保持 在並行開銷的閾值之內。CPU髙利用的狀況可能會有不少,這裏只給出幾個常見的類型及 對應的處理方法,在後續的章節中會陸續進行進一步的描述。

相關文章
相關標籤/搜索