(轉)SQL Server 性能調優(cpu)

摘自:http://www.cnblogs.com/Amaranthus/archive/2012/03/07/2383551.html php

 

研究cpu壓力工具html

perfomnode

SQL跟蹤ios

性能視圖sql

cpu相關的wait event數據庫

Signal wait timewindows

SOS_SCHEDULER_YIELD等待緩存

CXPACKET等待服務器

CMEMTHREAD等待session

調度隊列

cpu密集型查詢

CPU使用率的建立幾種情況

miss index

統計數據丟失

SARG謂詞

隱式類型轉化

參數探測器

ad hoc 非參數化查詢

修改源代碼

強制性參數化

不合適的併發查詢

cost threshold for parallelism

max degree of parallelism

超線程和併發查詢

診斷不合適的併發查詢

解決併發問

TokenAndPermUserStore

總結

參考資料:

 

cpusql server 中扮演了很重要的角色,雖然cpu綁定的服務器排除cpu問題相對比較簡單,但並不意味着老是簡單。若是你的1個或多個cpu滿負荷運行,那麼就要當心了。sql server cpu的使用無處不在,因此若是cpu滿負荷運行,那麼問題很嚴重。

cpu性能出現問題,通常很慢盤查爲啥,由於會照成cpu性能問題的不少,如內存不足,數據換進換出,cpu一路高。寫操做性能很爛,索引建的不合適,sql server 配置等問題都會引發cpu太高的問題。因此cpu性能盤查須要很當心和仔細。

無論是什麼問題引起的,對cpu的性能分析就是把問題隔離到一個特定資源,咱們可使用perfmon,性能視圖,還有sql跟蹤來收集資源。

一旦發生問題,咱們就要把問題鎖定在一個或多個查詢上,對其進行調整如調整cpu密集型的查詢,添加合適的索引,使用存儲過程替換ad hoc查詢等等。

 

研究cpu壓力工具

perfom

對於cpu壓力的研究咱們通常使用一下工具:perfmonSQL跟蹤,動態性能視圖

perfmon咱們能夠跟蹤以下性能指標:

Processor/ %Privileged Time                          --內核級別的cpu使用率

Processor/ %User Time                                   --用戶幾倍的cpu使用率

Process (sqlservr.exe)/ %Processor Time    --某個進程的cpu使用率

上面3個性能指標是全局範圍的,SQL Satatistics 計數器雖然不能直接說明cpu的使用率可是能夠間接的說明cpu的使用狀況。

 SQLServer:SQL Statistics/Auto-Param Attempts/sec

 SQLServer:SQL Statistics/Failed Auto-params/sec

 SQLServer:SQL Statistics/Batch Requests/sec

 SQLServer:SQL Statistics/SQL Compilations/sec

 SQLServer:SQL Statistics/SQL Re-Compilations/sec

 SQLServer:Plan Cache/Cache hit Ratio

這些計數器沒有額定的閥值,須要和性能基線作對

 

SQL跟蹤

SQL跟蹤的具體用法就很少講,不少人都已經會用了,SQL跟蹤在某個時間點上的捕獲遠遠不如動態性能視圖,並且捕獲的時候要注意設置過濾否則會捕獲大量無用的sql

 

性能視圖

性能視圖是分析的利器:

驗證cpu壓力的wait event 可使用 sys.dm_os_wait_stats.

經過sys.dm_os_wait_stats sys.dm_os_schedulers,經過wait event 類型診斷。

能夠用sys.dm_exec_query_statssys.dm_exec_sql_text說明使用大量cpu的執行計劃

可使用sys.dm_os_waiting_task查看cpu相關的等待類型

經過sys.dm_exec_requests查看當前正在的查詢的資源使用狀況

 

cpu相關的wait event

sql server 全部的等待信息,都會被記錄。可使用sys.dm_os_wait_stats中查看。這個視圖能夠用來肯定cpu壓力,查看cpu綁定系統中大多數的wait event

Signal wait time

根據特定的等待類型(wait type),有一些等待時間:

wait_time_ms該等待類型全部等待時間。

signal_wait_time_ms從發出信號到開始運行的時間差,時間花費在等待運行隊列中,是單純的cpu等待。

signal_wait_time_ms是全部等待時間的一個重要部分,說明了等待一個可用資源的等待時間。能夠表示sql server 中是否正在運行cpu密集型查詢。

下面代碼量化的像是signal_wait_time_ms佔的比重

SELECT SUM(signal_wait_time_ms) AS TotalSignalWaitTime ,

( SUM(CAST(signal_wait_time_ms AS NUMERIC(20, 2)))

/ SUM(CAST(wait_time_ms AS NUMERIC(20, 2))) * 100 )

AS PercentageSignalWaitsOfTotalTime

FROM sys.dm_os_wait_stats

這個dmv記錄了統計信息,系統重啓以後會被狀況,因此若是查看某一時間點狀況不是很好用,只能用臨近的統計相減,也能夠用 dbcc sqlperf清空統計信息。

關於session級和語句級wait event 能夠查看文章:http://sqlblog.com/blogs/jonathan_kehayias/archive/2010/12/30/an-xevent-a-day-30-of-31-tracking-session-and-statement-level-waits.aspx

咱們可使用sys.dm_os_wait_stats查看那個資源等待時間最長。top 10 用等待時間排序,可是這樣就容易忽略一開始的等待也就是signal wait time,所以要減去signal_wait_time,做爲等待調度器的時間。

下面討論三個wait type 這三個和cpu壓力息息相關。

SOS_SCHEDULER_YIELD等待

sql server 調度器是非搶佔式調度,也就是說是依靠查詢自動放棄cpu,可是windows是搶佔式,也就是說必定時間以後,windows 會直接從cpu上刪除任務。

當查詢自動放棄cpu,而且等待恢復執行,這個等待就叫作SOS_SCHEDULER_YIELD,若是這個值很小那麼就說明,花費在等待cpu上,而不是等待其餘資源上。

若是sys.dm_exec_requests或者 sys.dm_os_waiting_tasks SOS_SCHEDULER_YIELD等待值偏高,那麼說明有cpu密集型查詢,須要優化sql或者增長cpu

CXPACKET等待

當同步查詢進程,worker之間交換迭代器的時候發生CXPACKET等待,特別是發生併發查詢的時候。若是是在dw,或者是報表數據庫,那麼發生sql比較少,而且有大量的併發查詢能夠減小執行時間。對dw來講是正常的,可是在oltp中大多數是小的sql和事務,若是發生大量的併發,會致使性能降低。

CMEMTHREAD等待 

CMEMTHREAD等待就是等待被同步的內存對象。有一些對象支持查詢同時訪問,有些不支持。當一個查詢訪問一個對象時,其餘查詢就必須等待,這就是CMEMTHREAD等待

一般CMEMTHREAD等待不會很長時間。可是當內存出現問題後,cpu利用率和CMEMTHREAD等待都會變高,這是性能比較差的查詢引發的。

 

調度隊列

關於調度隊列最主要的視圖就是sys.dm_os_schedulers,視圖主要的二個指標之一是每一個調度器有幾個task,和運行隊列的長度。可運行隊列內都是等待cpu時鐘的task,其餘的taskcurrent_tasks_count內,都處於sleep或者在等待其餘資源。

SELECT scheduler_id ,

current_tasks_count ,

runnable_tasks_count

FROM sys.dm_os_schedulers

WHERE scheduler_id < 255

這些值沒有固定的閥值,只能經過性能基線來對比。固然這些值都是越低越好。若是可運行隊列越長那麼,signal time 的時間也就越長,就意味着可能cpu不足。

上面的sql過濾掉了一些 scheduler 由於其餘的是backupdac等調度器。

 

cpu密集型查詢

關於cpu密集型查詢,有2個性能視圖,sys.dm_exec_query_statssys.dm_exec_sql_textsys.dm_exec_query_stats統計了每一個查詢計劃的各種信息。如*_worker_timecpu花費的時間。*_elapsed_time:總共運行的時間。

下面的sql統計了前10個最費時間的查詢:

SELECT TOP ( 10 )

SUBSTRING(ST.text, ( QS.statement_start_offset / 2 ) + 1,

( ( CASE statement_end_offset

WHEN -1 THEN DATALENGTH(st.text)

ELSE QS.statement_end_offset

END - QS.statement_start_offset ) / 2 ) + 1)

AS statement_text ,

execution_count ,

total_worker_time / 1000 AS total_worker_time_ms ,

total_worker_time / 1000 ) / execution_count

AS avg_worker_time_ms ,

total_logical_reads ,

total_logical_reads / execution_count AS avg_logical_reads ,

total_elapsed_time / 1000 AS total_elapsed_time_ms ,

total_elapsed_time / 1000 ) / execution_count

AS avg_elapsed_time_ms ,

qp.query_plan

FROM sys.dm_exec_query_stats qs

CROSS APPLY sys.dm_exec_sql_text(qs.sql_handlest

CROSS APPLY sys.dm_exec_query_plan(qs.plan_handleqp

ORDER BY total_worker_time DESC

這個查詢並不會顯示全部的query,執行計劃是被保存在cache中的,當cache被淘汰,由於dbcc命令沒清理,數據庫狀態發生變化,數據庫配置發生變化等等,都會引發cache丟失的狀況。有一些查詢使用了recompile標示或者提示那就永遠不會被保留在cache中。

若是你要全局的分析執行計劃,請使用sql跟蹤,而不要事情清空緩存,特別是在生產庫中,緩存一旦被清空在一點時間內,講嚴重影響性能。

 

CPU使用率的建立幾種情況

無論在服務器硬件配置和技術上面花了多大的成本,總有怎麼一些查詢會致使服務器的資源滿負荷運行。每一個sql被執行的時候,sql server優化器終會找一個儘可能高效的方式來獲取數據。若是當一個查詢miss index或者忽略了合適的索引,那麼優化器就沒法生存一個真正高效的執行計劃。若是優化器相關的信息是不許確的,那麼優化器生存的執行計劃也是不許備的,由於關於成本的計算也是不許確的。另一種情況就是優化器生存的結果對一個查詢是優化的,可是對其餘查詢並不優化。由於不合適的參數探測致使了這個問題。

miss index

miss index 是照成大量cpuio使用的情況之一,也是最常發生的情況。當前的索引並不能知足查詢的時候,優化器會試圖是用表掃描來完成,這樣就照成了大量的非必須的數據參與到預算中,會照成cpuio的極大浪費。那麼咱們就以 adventureworks2008 數據庫做爲例子

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.SalesOrderID = sod.SalesOrderID

INNER JOIN Production.Product AS p ON sod.ProductID = p.ProductID

INNER JOIN Sales.Customer AS c ON soh.CustomerID = c.CustomerID

INNER JOIN Person.Person AS per

ON c.PersonID = per.BusinessEntityID

WHERE LineTotal > 25000

這個查詢在salesorderdetail使用了表掃描,由於並無關於linetotal列的索引

SQL Server parse and compile time:

CPU time = 0 ms, elapsed time = 0 ms.

SQL Server Execution Times:

CPU time = 452 ms, elapsed time = 458 ms.

雖然返回24行只用了半秒的時間可是仍是不夠優化。那麼咱們就在linetotal建一個索引

CREATE NONCLUSTERED INDEX idx_SalesOrderDetail_LineTotal

ON Sales.SalesOrderDetail (LineTotal)

那麼咱們繼續運行上面的sql

SQL Server parse and compile time:

CPU time = 0 ms, elapsed time = 0 ms.

SQL Server Execution Times:

CPU time = 0 ms, elapsed time = 8 ms.

結果有很大的不通,經過這個簡單的例子說明cpu的壓力有可能且很大的可能都是miss index 照成的。

 

統計數據丟失

優化器會經過統計信息估計每一個查詢操做的基數。經過估計行數,操做的花費。操做的花費決定了整個計劃的花費。若是統計信息不許確,那麼優化器的成本計算也就不許確,這樣就會致使優化器誤判,估計的花費是低的可是並不必定實際的花費也是低的。一般統計值不許確是比實際值要小,一旦小,那麼優化器就會選擇比較適合較小數量的操做符如nest loopkey lookup,可是實際的數據量很大,這樣就會對查詢照成嚴重的影響。有一個方法查看統計值是否丟失,就是在ssms中運行實際的查詢計劃,而且對比估計值和實際值的差距,若是差距很大那麼就是統計數據丟失了,須要及時更新統計值。固然能夠經過 update statistics 更新統計值,詳細的用法能夠參見聯機文檔。

若是是統計值過時的問題照成的那麼有一下3個方法:

1.把數據庫設置爲自動更新統計值。

2.若是自動更新統計信息無效,那麼有多是索引創建的時候有不計算統計值的標記。

3.建立一個腳本定時更新統計值。

 

SARG謂詞

就是不要再表的字段上使用函數或者計算,由於你一用,就沒辦法使用索引了。不能使用索引,顯而易見cpu高了,io堵塞了。

 

隱式類型轉化

不少人都認爲隱式轉化沒什麼關係,並不會給性能帶來多大的衝擊。一個過濾若是類型不一樣那麼sql server 是沒法比較的,這時候就要隱式轉化了,隱式轉化的時候都是從低的優先級轉化到高的優先級,好比若是一個是varchar一個是nvarchar那麼就會把varchar隱式轉化成nvarchar。問題就來了若是一個表列是varchar可是過濾的條件是nvarchar,那麼就會隱式轉化把varchar轉化成爲nvarchar那麼就會發生非SARG謂詞,沒法使用索引查找了。下面有個例子:

SELECT p.FirstName ,

p.LastName ,

c.AccountNumber

FROM Sales.Customer AS c

INNER JOIN Person.Person AS p ON c.PersonID = p.BusinessEntityID

WHERE AccountNumber = N'AW00029594'

固然 accountnumber 上是有索引的

就變成索引掃描了,我使用2008r2測試,結果不是索引掃描。可是當我把accountnumber 禁用掉以後,盡然和書上發的執行計劃是同樣的了,讓我深深的懷疑,是否是做者在寫書的時候,把accountnumber 禁用了而沒發現呢?我在網上查了寫資料,發現了在sql server 2000下的測試語句ok,在2000 下面是會照成索引掃描。因此你們若是用2008r2的就不須要太擔憂這個問題。若是在其餘版本真的遇到這個問題那麼如何解決呢?那麼就把類型轉化放在常量這一端。或者直接修改表的數據類型。

我把2000的測試語句發出來:

DECLARE @CustID NCHAR(5)

SET @CustID = N'FOLKO'

SELECT CompanyName FROM NorthWind.dbo.Customers WHERE CustomerID = @CustID

這裏要注意由於 customers 表的結構是 nchar的因此咱們在測試的時候先要修改掉這個數據類型,改成charnorthwind裏面有外鍵要通通刪掉,主鍵須要重建。

說到這裏,我就和書的做者聯繫了,根據他給的結論,和測試結果

-- Windows Collation will get a Seek
CREATE TABLE #T (col1 varchar(10) COLLATE Latin1_General_CI_AS PRIMARY KEY);
SELECT * 
FROM #T 
WHERE col1 = N'q'

-- SQL Collation will get a Scan
CREATE TABLE #T2 (col1 varchar(10) COLLATE SQL_Latin1_General_CP1_CI_AI PRIMARY KEY);
SELECT * 
FROM #T2 
WHERE col1 = N'q'

-- Your Collation will get a Seek
CREATE TABLE #T3 (col1 varchar(10) COLLATE Chinese_PRC_CI_AS PRIMARY KEY);
SELECT * 
FROM #T3 
WHERE col1 = N'q'

DROP TABLE #T
DROP TABLE #T2
DROP TABLE #T3

當你用SQL Server 的排序規則那麼就是掃描若是用windows 的排序規則那麼就是查詢。

上面就是他發過來的sample

 

參數探測器

sql server爲存儲過程,函數或者參數化查詢建立執行計劃的時候,會探測參數,並結合統計數據計算花費選擇較好的執行計劃。參數探測器只會在編譯或者重編譯的時候發生,那麼這裏就有個問題若是當建立執行計劃的時候該參數的值是非典型的,那麼就極可能並不適用於之後傳過來的參數。初始化編譯的時候,只有輸入的參數會被探測,本地變量是不會被探測的。若是一個語句在一個batch 中被重編譯那麼參數和變量都會被探測。

下面是一個運行在Adventureworks數據庫的例子最大日期是2011-7-8 最小日期是2004-8-7.

CREATE PROCEDURE user_GetCustomerShipDates

(

@ShipDateStart DATETIME ,

@ShipDateEnd DATETIME

)

AS

SELECT CustomerID ,

SalesOrderNumber

FROM Sales.SalesOrderHeader

WHERE ShipDate BETWEEN @ShipDateStart AND @ShipDateEnd

GO

會對shipdate進行過濾那麼就在shipdate設置一個索引

CREATE NONCLUSTERED INDEX IDX_ShipDate_ASC

ON Sales.SalesOrderHeader (ShipDate)

GO

接下來會運行2次這個存儲過程第一次誇多年的,第二次就誇幾天。並查看實際的執行計劃

DBCC FREEPROCCACHE

EXEC user_GetCustomerShipDates '2001/07/08', '2004/01/01'

EXEC user_GetCustomerShipDates '2001/07/10', '2001/07/20'

查詢結果2個都用了掃描

       |--Filter(WHERE:([AdventureWorks].[Sales].[SalesOrderHeader].[ShipDate]>=[@ShipDateStart] AND [AdventureWorks].[Sales].[SalesOrderHeader].[ShipDate]<=[@ShipDateEnd]))
            |--Compute Scalar(DEFINE:([AdventureWorks].[Sales].[SalesOrderHeader].[SalesOrderNumber]=[AdventureWorks].[Sales].[SalesOrderHeader].[SalesOrderNumber]))
                 |--Compute Scalar(DEFINE:([AdventureWorks].[Sales].[SalesOrderHeader].[SalesOrderNumber]=isnull(N'SO'+CONVERT(nvarchar(23),[AdventureWorks].[Sales].[SalesOrderHeader].[SalesOrderID],0),N'*** ERROR ***')))
                      |--Table Scan(OBJECT:([AdventureWorks].[Sales].[SalesOrderHeader]))

這個是個人結果和書上的不同。那麼爲何爲產生表掃描不是索引查找呢,由於第一個查詢在編譯的時候優化器任務用表掃描比較合適。可是到第二句的時候,雖然是不合適,可是已經有執行計劃存儲在了內存裏面,sql server 就直接拿來用了,就照成了這個問題。開 SET STATISTICS IO on

'SalesOrderHeader'。掃描計數1,邏輯讀取700 次,物理讀取0 次,預讀0 次,lob 邏輯讀取0 次,lob 物理讀取0 次,lob 預讀0 次。

那麼咱們把2個存儲過程倒過來:

DBCC FREEPROCCACHE

EXEC user_GetCustomerShipDates '2001/07/10', '2001/07/20'

EXEC user_GetCustomerShipDates '2001/07/08', '2004/01/01'

狀況就徹底不同了

       |--Compute Scalar(DEFINE:([AdventureWorks].[Sales].[SalesOrderHeader].[SalesOrderNumber]=[AdventureWorks].[Sales].[SalesOrderHeader].[SalesOrderNumber]))
            |--Nested Loops(Inner Join, OUTER REFERENCES:([Bmk1000], [Expr1004]) WITH UNORDERED PREFETCH)
                 |--Index Seek(OBJECT:([AdventureWorks].[Sales].[SalesOrderHeader].[IDX_ShipDate_ASC]), SEEK:([AdventureWorks].[Sales].[SalesOrderHeader].[ShipDate] >= [@ShipDateStart] AND [AdventureWorks].[Sales].[SalesOrderHeader].[ShipDate] <= [@ShipDateEnd]) ORDERED FORWARD)
                 |--Compute Scalar(DEFINE:([AdventureWorks].[Sales].[SalesOrderHeader].[SalesOrderNumber]=isnull(N'SO'+CONVERT(nvarchar(23),[AdventureWorks].[Sales].[SalesOrderHeader].[SalesOrderID],0),N'*** ERROR ***')))
                      |--RID Lookup(OBJECT:([AdventureWorks].[Sales].[SalesOrderHeader]), SEEK:([Bmk1000]=[Bmk1000]) LOOKUP ORDERED FORWARD)

果斷使用了索引查找,可是對第二句來講索引查找不必定是好事情,由於他要掃描的行太多,若是假定如今樹是3層,那麼讀一個頁須要3次你想一想。

'SalesOrderHeader'。掃描計數1,邏輯讀取17155 次,物理讀取0 次,預讀0 次,lob 邏輯讀取0 次,lob 物理讀取0 次,lob 預讀0 次。

比較一下誇多年的那個存儲過程的邏輯讀。

一般keylookup只適合較少的數據一般是整表的1%,固然不是絕對的。

跟蹤標記4136

SQL Server 2008 引入了一個新的跟蹤標記 4316,使用了這個跟蹤標記以後sql server 會關掉參數探測功能,這個功能在sql server 2008 sp2 cu7 ,sql server 2--8 r2 cu2,sql server 2005 sp3 cu9 中才加入。先前討論過了若是開了參數探測,一個存儲過程若是第一次編譯的時候估計值偏小,或者偏大,都會對接下來使用這個存儲過程產生影響。當參數探測器被停用的時候 4316 跟蹤是如何處理的呢,舉個例子這裏又一個列 X 有以下的值1,2,3,3,3,3,3,4,5,5,那麼他的估計值就是2這個哪來的?就是全部數據的平均估計值。全部的計劃都會被這個值優化。若是開了這個選項那麼會給不少存儲過程優化帶來好處。

這邊有篇關於4316的文章比較簡單可是很到位:http://blogs.msdn.com/b/axperf/archive/2010/05/07/important-sql-server-change-parameter-sniffing-and-plan-caching.aspx

使用 OPTIMIZE FOR 提示

到了sql server 2005 之後你可使用OPTIMIZE FOR 來優化查詢

CREATE PROCEDURE user_GetCustomerShipDates

(

@ShipDateStart DATETIME ,

@ShipDateEnd DATETIME

)

AS

SELECT CustomerID ,

SalesOrderNumber

FROM Sales.SalesOrderHeader

WHERE ShipDate BETWEEN @ShipDateStart AND @ShipDateEnd

OPTION ( OPTIMIZE FOR ( @ShipDateStart = '2001/07/08',

@ShipDateEnd = '2004/01/01' ) )

GO

使用了OPTIMIZE FOR 提示那麼sql server 就會按提示的信息來編譯,固然若是提示的值不理想那麼也會產生問題。

SQL Server 2008 中引入了一個新的提示 OPTIMIZE FOR UNKNOWN,那麼sql server 就不會再用參數探測的功能,它的功效和4316相同,因此這個方法是比較可取的由於畢竟參數探測仍是一個比較好的東西。

重編譯選項

重編譯也是解決參數探測的一個方法,可是問題就是執行計劃不會被保存在內存中,可是就有一個問題存儲過程的執行的花費就會變高。

CREATE PROCEDURE user_GetCustomerShipDates

(

@ShipDateStart DATETIME ,

@ShipDateEnd DATETIME

)

WITH RECOMPILE

AS

SELECT CustomerID ,

SalesOrderNumber

FROM Sales.SalesOrderHeader

WHERE ShipDate BETWEEN @ShipDateStart AND @ShipDateEnd

GO

若是存儲過程當中只須要一部分從新編譯,那麼就可使用OPTION(RECOMPILE)選項放到查詢中便可,相比重編譯整個存儲過程,這樣會好些。

CREATE PROCEDURE user_GetCustomerShipDates

(

@ShipDateStart DATETIME ,

@ShipDateEnd DATETIME

)

AS

SELECT CustomerID ,

SalesOrderNumber

FROM Sales.SalesOrderHeader

WHERE ShipDate BETWEEN @ShipDateStart AND @ShipDateEnd

OPTION ( RECOMPILE )

GO

 

ad hoc 非參數化查詢

Ad hoc查詢語句發送到sql server 的時候優化器仍是會從cache查找合適的執行計劃。ad hoc 查詢會讓全部的語句都生產一遍執行計劃,這樣會照成資源浪費特別是CPU

SELECT soh.SalesOrderNumber ,

sod.ProductID

FROM Sales.SalesOrderHeader AS soh

INNER JOIN Sales.SalesOrderDetail AS sod

ON soh.SalesOrderID = sod.SalesOrderID

WHERE soh.SalesOrderNumber = 'SO43662'

SELECT soh.SalesOrderNumber ,

sod.ProductID

FROM Sales.SalesOrderHeader AS soh

INNER JOIN Sales.SalesOrderDetail AS sod

ON soh.SalesOrderID = sod.SalesOrderID

WHERE soh.SalesOrderNumber = 'SO58928'

SELECT soh.SalesOrderNumber ,

sod.ProductID

FROM Sales.SalesOrderHeader AS soh

INNER JOIN Sales.SalesOrderDetail AS sod

ON soh.SalesOrderID = sod.SalesOrderID

WHERE soh.SalesOrderNumber = 'SO70907'

很不幸,這三個語句原本是應該能夠用同一個執行計劃的。如今由於ad hoc 用不了了。若是是簡單的查詢那麼sql server 會使用簡單參數化來重用執行計劃。可是上面的例子太複雜了因此沒辦法。那就會有2個問題

1.執行計劃緩存充滿了單用戶的計劃,不能被重用。浪費內存空間。

2.執行計劃由於可用因此老是要編譯新的計劃,致使cpu時鐘浪費。

能夠用perfmon來監視編譯重編譯的量

 SQLServer: SQL Statistics: SQL Compilations/Sec

 SQLServer: SQL Statistics: Auto-Param Attempts/Sec

 SQLServer: SQL Statistics: Failed Auto-Param/Sec

若是真的是非參數化照成的問題,那麼又不少方法去調整,最好的方式是修改源代碼。若是不行那麼只能設置sql server 來調整

修改源代碼

關於修改源代碼就不討論了,直接給demo本身看。

cmd.CommandType = CommandType.Text;

cmd.CommandText = @"SELECT soh.SalesOrderNumber,

sod.ProductID

FROM Sales.SalesOrderHeader AS soh

INNER JOIN Sales.SalesOrderDetail AS sod

ON soh.SalesOrderID = sod.SalesOrderID

WHERE soh.SalesOrderNumber = '" + txtSalesOrderNo.Text + "'";

dtrSalesOrders = cmd.ExecuteReader();

dtrSalesOrders.Close();

cmd.CommandType = CommandType.Text;

cmd.CommandText = @"SELECT soh.SalesOrderNumber,

sod.ProductID

FROM Sales.SalesOrderHeader AS soh

INNER JOIN Sales.SalesOrderDetail AS sod

ON soh.SalesOrderID = sod.SalesOrderID

WHERE soh.SalesOrderNumber = @SalesOrderNo";

cmd.Parameters.Add("@SalesOrderNo", SqlDbType.NVarChar, 50);

cmd.Parameters["@SalesOrderNo"].Value = txtSalesOrderNo.Text;

dtrSalesOrders = cmd.ExecuteReader();

強制性參數化

關於強制參數化,能夠設置數據庫選項

ALTER DATABASE AdventureWorks SET PARAMETERIZATION FORCED

若是使用強制參數化那麼上面咱們提過的3sql的執行計劃就變成一個了。可使用以下sql查詢

SELECT b.text,c.* FROM sys.dm_exec_query_stats   a

      CROSS APPLY sys.dm_exec_sql_text(a.sql_handle) b

      CROSS APPLY  sys.dm_exec_query_plan(a.plan_handle) c  

使用強制參數化很很差,就會使得全部的sql都使用同一個查詢計劃,無論好壞,有點和參數探測器的問題相似了。

Optimize for ad hoc workloads

這是一個數據庫服務配置項,配置了以後當ad hoc第一次運行的時候sql server 會產生個子查詢計劃不能用,當第二次執行的時候產生一個執行計劃。能夠有效的減小內存壓力。

EXEC sp_configure 'show advanced options',1

RECONFIGURE

EXEC sp_configure 'optimize for ad hoc workloads',1

RECONFIGURE

 

不合適的併發查詢

當查詢在不一樣的線程,每一個線程在不一樣的調度器下運行,就能夠理解爲併發查詢。

當一個查詢被提交到sql server 優化器,優化器開始估算花費,若是花費比cost threshold for parallelism 要大,那麼優化器會考慮使用併發。max degree of parallelism 用來限制查詢的最大併發數若是查詢中使用了maxdop提示的話那麼最大併發數則爲提示的值。併發查詢經過把數據水平分區到各個不一樣的邏輯cpu,經過多個處理器內核執行相同的操做來減小查詢的時間。這個對於dw或者報表數據庫是頗有用的由於數據量很大,並且併發請求比較少。因此可以充分的利用硬件資源,而且減小執行的時間。對於併發的負載仍是又一些要素,並非指餘下的設備資源可否應付併發負載帶來的大內存分配和磁盤io的問題。併發查詢使用的好會給服務器的總體性能帶來很大的提高,可是併發負載對oltp系統來講是很是不利的,oltp是又不少小的事務組成,併發量比較大,若是oltp上有併發負載,佔據了較長時間的cpu,那麼其餘事務就會等待併發的完成,致使查詢假死在那邊。

對於併發的配置參數有2cost threshold for parallelism max degree of parallelism 第一個是啓用併發查詢的閥值,第二個是最大併發數。當發生不合適的併發的時候,建議的解決方法是調整max degree of parallelism,減小1/2,或者減小1/4或者直接設置爲1。固然這個是不理想的解決方案,最理想的解決方案是設置2個配置參數,到一個比較合理的值。

cost threshold for parallelism 

cost threshold for parallelism 是一個啓用併發的閥值,查過了就啓用併發,沒超過就不啓用。cost threshold for parallelism 的默認值是5秒,可是對於大數據庫5秒是一個比較小的值,所以設置cost threshold for parallelism 閥值很重要

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED ;

WITH XMLNAMESPACES

(DEFAULT 'http://schemas.microsoft.com/sqlserver/2004/07/showplan')

SELECT query_plan AS CompleteQueryPlan ,

n.value('(@StatementText)[1]', 'VARCHAR(4000)') AS StatementText ,

n.value('(@StatementOptmLevel)[1]', 'VARCHAR(25)')

AS StatementOptimizationLevel ,

n.value('(@StatementSubTreeCost)[1]', 'VARCHAR(128)')

AS StatementSubTreeCost ,

n.query('.') AS ParallelSubTreeXML ,

ecp.usecounts ,

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="Parallelism"]') = 1

因此經過以上查詢,分析類似的查詢。以最小化cpuio競爭爲目標設置cost threshold for parallelism

max degree of parallelism

sql server 併發查詢的併發度有如下3點:

1.可用的處理器數量

2.max degree of parallelism

3.MAXDOP查詢提示

若是你的服務器如今出現了併發問題那麼修改閥值和最大併發度是解決這個問的最快速的方法。

網上有種說法就是直接把max degree of parallelism設爲1,對於oltp系統的特性是可能性的,可是仍是以爲你這樣設置以後就不能使用併發了,感受會減小性能。

分析CXPACKETwait eventCXPACKET只是一種症狀,並無真正的發生問題。查看sys.dm_os_waiting_tasks中其餘的wait event能夠更好的得出合適的 max degree of parallelism。若是相關的等待事件是PAGEIOLATCH_SH,併發正在等待io讀取,減小max degree of parallelism 並不能解決根本問題,它只會減少被使用的工做任務,減小CXPACKET累計等待時間。可是也可能會減小額外的io,給你提示io性能的空間。

併發查詢也須要考慮到內存的結構體系,在NUMA結構下,最大併發度設置在一個NUMA節點的可用常常。這樣node之間就不會產生交互,由於node間的共享內存操做代價很高。在SMP結構中,多個處理器內核都在單個芯片上共享二級緩存,這樣很容易照成內存命中率降低,可是好處是在併發查詢下高併發的性能表現很好,固然max degree of parallelism 也要根據硬件設備的能力作適當的調節。在sql server 2008 以上的版本還可使用資源管理器來限制。

超線程和併發查詢

超線程是Intel一個技術,爲了提升併發操做,就設計了2個邏輯內核對於1個物理內核。就是說不想之前一個調度器一個物理內核,如今2個內核,而且能夠同時使用。固然咱們關心的是性能,那麼sql server 有沒有使用超線程,會給sql server 帶來什麼影響。

對於olapdss系統併發查詢是又很大好處的,可是當開了超線程的時候性能就變差了。可是超線程對oltp沒什麼影響,對於oltp來講超線程在增長併發度是又好處的。對於早期的超線程由於會帶來不少問題因此dba都是在bios中關閉超線程的。近幾年sql server 2008 發佈了建議關閉超線程特別是olap/dw/dss系統。超線程最大的問題是超線程會共享內置的cache,照成命中率降低。如今不少問題都解決了,windows 2003 就能認識物理內核和邏輯內核,而且給予不一樣的工做量。如今的處理器緩存變大不容易發生。事實上對於當前的處理器結構,特別是intel nehalem開超線程是有好處的,除非是有明確的理由。因此在決定是否使用超線程的時候最好先作一下測試。

診斷不合適的併發查詢

最好診斷的方法是查看wait統計信息和latch統計信息,當執行併發的時候出現瓶頸,CXPACKET等待就會變的很高。當併發查詢等待交換迭代器到另一個工做任務的時候就會發生等待。一般這裏也會有一些相關的其餘等待,來協助工做,由於大量的併發查詢,CXPACKET的等待會比根本緣由蓋過去。最好的方法是分隔在troubleshooting各個相關的等待時間。由於併發查詢會影響全局的性能問題。CXPACKET頗有可能只是一個症狀不少問題都會引發CXPACKET偏高。當io不能維持併發查詢的需求,關鍵的等待多是IO_COMPLETION,ASYNC_IO_COMPLETION,PAGEIOLATCH_*,不能擴展io性能。可是減少併發度,任然會發生io性能瓶頸的情況,那麼就要提從全局的系統性能。若是CXPACKE相關的等待是LATCH_*,SOS_SCHEDULER_YIELD,那麼頗有多是併發的問題,深刻latch驗證是併發的問題。sys.dm_os_latch_stats包含一些特殊的latch等待,如ACCESS_METHODS_DATASET_PARENT,LATCH_*,SOS_SCHEDULER_YIELD等待都比較高,那麼減小併發度就可能解決問題。

解決併發問題

先前已經討論過,對於大的,長運行時間的查詢使用併發頗有好處。不合適的併發主要問題是負載類型是混合的。不少庫本質上是oltp的可是由於sql比較複雜超過了cost threshold for parallelism。因此試圖提高一下cpu性能。若是診斷到了併發存在問題,若是沒有被調整過,那麼頗有可能由於索引丟失或者不合適的索引形成問題,若是調整完以後仍是這樣那麼就用先前提到的2個系統配置參數,來全局的管理數據庫併發。

TokenAndPermUserStore

TokenAndPermUserStore2005的時候被引進來優化關於權限驗證,怎麼TokenAndPermUserStore是怎麼工做的呢?這裏有一個簡單的例子說明TokenAndPermUserStore的工做狀況。例子當你執行的時候select * from t1 join t2 join t3,那麼sql sever 就會對權限進行驗證,驗證後會緩存在TokenAndPermUserStore以避免之後重複驗證。可是這個會引發性能問題,特別是較早版本的sql server 2005,由於這個cache的內存限制太高性能問題的表現爲cpu使用率比較高,cmemthread等待比較嚴重。微軟已經給出了一個解決方案http://support.microsoft.com/kb/927396/一般問題發生在非awe內存分配的sql server上(特別是64b的服務器),不少動態的或者 adhoc查詢,數據庫用戶過多。你可使用以下sql查詢TokenAndPermUserStore使用量:

SELECT SUM(single_pages_kb + multi_pages_kb) / 1024.0 AS CacheSizeMB

FROM sys.dm_os_memory_clerks

WHERE [name] = 'TokenAndPermUserStore'

若是cache一直增加,而且伴隨着cmemthread等待,那麼頗有可能致使高cpu使用率,若是使用sql server2005低於sp2補丁,那麼第一時間就是打上補丁。嫌少動態sqladhoc來減小發生問題的機率。

短時間修復

使用sysadmin角色,由於sysadminsql server 最大的權限,不須要作權限檢查。那麼也就不會產生cache

按期清理cacheDBCC FREESYSTEMCACHE ('TokenAndPermUserStore')

sql 2005 sp2 以上版本使用 trace flage 4618,4610來限制cache中的條目數量,當4618開啓,cache中只能有1024cache,當2trace flag 都開啓那麼又8192個條目。這個限制會影響其餘cache,所以只能臨時使用。sql server 2005 sp3之後有個新的trace flag 4612,能夠設置客戶端的配額詳細看:(http://support.microsoft.com/kb/959823)

sql2008的配置項

sql server 2008 對於TokenAndPermUserStore2個配置項,access check cache quotaaccess check cache bucket count,若是問題很明顯的發生,那麼就減小這2個值的大小,其實並不建議修改默認值,除非又微軟客服支持。

 

總結

troubleshooting是一個分析問題的過程,我上一篇文章也說了,是一個根據統計的信息,分析問題的過程。所以須要瞭解數據庫內核,內部運行的結構才能更好的進行調優。調優第一步的信息每每都是來至於perfmon,和動態性能視圖,最後纔是sqltrace,爲啥,由於sqltrace最浪費時間,會有滯後性,因此已經滯後了還不如放到最後運行。

 

參考資料:

 Implicit data conversations
•http://sqlblog.com/blogs/jonathan_kehayias/archive/2010/01/08/findingimplicit-column-conversions-in-the-plan-cache.aspx
 Query tuning
• http://www.straightpathsql.com/presentations/ucandoit/
• http://www.simple-talk.com/sql/performance/simple-query-tuning-with-statistics-io-and-execution-plans/

•http://www.simple-talk.com/sql/t-sql-programming/13-things-youshould-know-about-statistics-and-the-query-optimizer/
• http://www.simple-talk.com/author/gail-shaw/
 Estimated vs. actual row counts
• http://sqlinthewild.co.za/index.php/2009/09/22/estimated-rows-actual-rows-and-execution-count/
 Cost threshold for parallelism
• http://sqlblog.com/blogs/jonathan_kehayias/archive/2010/01/26/21172.aspx
• Max degree of parallelism
• http://msdn.microsoft.com/en-us/library/ms181007.aspx
 Query hints
• http://msdn.microsoft.com/en-us/library/ms181714.aspx
 Guidelines for modifying MAXDOP
• http://support.microsoft.com/kb/329204
 Limiting MAXDOP with the Resource Governor
•http://www.sqlmag.com/blog/sql-server-questions-answered-28/database-administration/controlling-maxdop-executing-queries-140163
 Parallelism/MAXDOP configuration
• http://msdn.microsoft.com/en-us/library/ms178065.aspx
• http://msdn.microsoft.com/en-us/library/ms188611.aspx
• http://blogs.msdn.com/b/joesack/archive/2009/03/18/should-you-worryabout-sos-scheduler-yield.aspx

 SQLOS architecture
• http://blogs.msdn.com/b/sqlosteam/archive/2010/06/23/sqlos-resources.aspx
•http://sqlblogcasts.com/blogs/sqlworkshops/archive/2007/11/25/findingoptimal-number-of-cpus-for-a-given-long-running-cpu-intensive-dss-olaplike-queries-workload.aspx
 System Monitor CPU counters
• http://msdn.microsoft.com/en-us/library/ms178072.aspx
 DMV usage for CPU usage from ring buffers
•http://troubleshootingsql.com/2009/12/30/how-to-find-out-the-cpuusage-information-for-the-sql-server-process-using-ring-buffers/
• http://msdn.microsoft.com/en-us/library/ms175048(SQL.90).aspx
• http://technet.microsoft.com/en-us/library/cc966540.aspx
 Forced parameterization
• http://technet.microsoft.com/en-us/library/ms175037(SQL.90).aspx
 Fixing TokenAndPermUserStore problems Identification and overview
• http://support.microsoft.com/kb/927396
 Access check result cache
• http://support.microsoft.com/kb/955644
• http://msdn.microsoft.com/en-us/library/cc645588.aspx
• Purging the cache whenever it reaches a certain size
• http://blogs.msdn.com/chrissk/archive/2008/06/19/script-to-purgetokenandpermuserstore.aspx

 

 SQL Server 2008 sp_configure options
• http://support.microsoft.com/kb/955644/en-us
• Hot-fixes associated with this problem
• http://support.microsoft.com/kb/959823

 

研究cpu壓力工具

perfom

SQL跟蹤

性能視圖

cpu相關的wait event

Signal wait time

SOS_SCHEDULER_YIELD等待

CXPACKET等待

CMEMTHREAD等待

調度隊列

cpu密集型查詢

CPU使用率的建立幾種情況

miss index

統計數據丟失

SARG謂詞

隱式類型轉化

參數探測器

ad hoc 非參數化查詢

修改源代碼

強制性參數化

不合適的併發查詢

cost threshold for parallelism

max degree of parallelism

超線程和併發查詢

診斷不合適的併發查詢

解決併發問

TokenAndPermUserStore

總結

參考資料:

 

cpusql server 中扮演了很重要的角色,雖然cpu綁定的服務器排除cpu問題相對比較簡單,但並不意味着老是簡單。若是你的1個或多個cpu滿負荷運行,那麼就要當心了。sql server cpu的使用無處不在,因此若是cpu滿負荷運行,那麼問題很嚴重。

cpu性能出現問題,通常很慢盤查爲啥,由於會照成cpu性能問題的不少,如內存不足,數據換進換出,cpu一路高。寫操做性能很爛,索引建的不合適,sql server 配置等問題都會引發cpu太高的問題。因此cpu性能盤查須要很當心和仔細。

無論是什麼問題引起的,對cpu的性能分析就是把問題隔離到一個特定資源,咱們可使用perfmon,性能視圖,還有sql跟蹤來收集資源。

一旦發生問題,咱們就要把問題鎖定在一個或多個查詢上,對其進行調整如調整cpu密集型的查詢,添加合適的索引,使用存儲過程替換ad hoc查詢等等。

 

研究cpu壓力工具

perfom

對於cpu壓力的研究咱們通常使用一下工具:perfmonSQL跟蹤,動態性能視圖

perfmon咱們能夠跟蹤以下性能指標:

Processor/ %Privileged Time                          --內核級別的cpu使用率

Processor/ %User Time                                   --用戶幾倍的cpu使用率

Process (sqlservr.exe)/ %Processor Time    --某個進程的cpu使用率

上面3個性能指標是全局範圍的,SQL Satatistics 計數器雖然不能直接說明cpu的使用率可是能夠間接的說明cpu的使用狀況。

 SQLServer:SQL Statistics/Auto-Param Attempts/sec

 SQLServer:SQL Statistics/Failed Auto-params/sec

 SQLServer:SQL Statistics/Batch Requests/sec

 SQLServer:SQL Statistics/SQL Compilations/sec

 SQLServer:SQL Statistics/SQL Re-Compilations/sec

 SQLServer:Plan Cache/Cache hit Ratio

這些計數器沒有額定的閥值,須要和性能基線作對

 

SQL跟蹤

SQL跟蹤的具體用法就很少講,不少人都已經會用了,SQL跟蹤在某個時間點上的捕獲遠遠不如動態性能視圖,並且捕獲的時候要注意設置過濾否則會捕獲大量無用的sql

 

性能視圖

性能視圖是分析的利器:

驗證cpu壓力的wait event 可使用 sys.dm_os_wait_stats.

經過sys.dm_os_wait_stats sys.dm_os_schedulers,經過wait event 類型診斷。

能夠用sys.dm_exec_query_statssys.dm_exec_sql_text說明使用大量cpu的執行計劃

可使用sys.dm_os_waiting_task查看cpu相關的等待類型

經過sys.dm_exec_requests查看當前正在的查詢的資源使用狀況

 

cpu相關的wait event

sql server 全部的等待信息,都會被記錄。可使用sys.dm_os_wait_stats中查看。這個視圖能夠用來肯定cpu壓力,查看cpu綁定系統中大多數的wait event

Signal wait time

根據特定的等待類型(wait type),有一些等待時間:

wait_time_ms該等待類型全部等待時間。

signal_wait_time_ms從發出信號到開始運行的時間差,時間花費在等待運行隊列中,是單純的cpu等待。

signal_wait_time_ms是全部等待時間的一個重要部分,說明了等待一個可用資源的等待時間。能夠表示sql server 中是否正在運行cpu密集型查詢。

下面代碼量化的像是signal_wait_time_ms佔的比重

SELECT SUM(signal_wait_time_ms) AS TotalSignalWaitTime ,

( SUM(CAST(signal_wait_time_ms AS NUMERIC(20, 2)))

/ SUM(CAST(wait_time_ms AS NUMERIC(20, 2))) * 100 )

AS PercentageSignalWaitsOfTotalTime

FROM sys.dm_os_wait_stats

這個dmv記錄了統計信息,系統重啓以後會被狀況,因此若是查看某一時間點狀況不是很好用,只能用臨近的統計相減,也能夠用 dbcc sqlperf清空統計信息。

關於session級和語句級wait event 能夠查看文章:http://sqlblog.com/blogs/jonathan_kehayias/archive/2010/12/30/an-xevent-a-day-30-of-31-tracking-session-and-statement-level-waits.aspx

咱們可使用sys.dm_os_wait_stats查看那個資源等待時間最長。top 10 用等待時間排序,可是這樣就容易忽略一開始的等待也就是signal wait time,所以要減去signal_wait_time,做爲等待調度器的時間。

下面討論三個wait type 這三個和cpu壓力息息相關。

SOS_SCHEDULER_YIELD等待

sql server 調度器是非搶佔式調度,也就是說是依靠查詢自動放棄cpu,可是windows是搶佔式,也就是說必定時間以後,windows 會直接從cpu上刪除任務。

當查詢自動放棄cpu,而且等待恢復執行,這個等待就叫作SOS_SCHEDULER_YIELD,若是這個值很小那麼就說明,花費在等待cpu上,而不是等待其餘資源上。

若是sys.dm_exec_requests或者 sys.dm_os_waiting_tasks SOS_SCHEDULER_YIELD等待值偏高,那麼說明有cpu密集型查詢,須要優化sql或者增長cpu

CXPACKET等待

當同步查詢進程,worker之間交換迭代器的時候發生CXPACKET等待,特別是發生併發查詢的時候。若是是在dw,或者是報表數據庫,那麼發生sql比較少,而且有大量的併發查詢能夠減小執行時間。對dw來講是正常的,可是在oltp中大多數是小的sql和事務,若是發生大量的併發,會致使性能降低。

CMEMTHREAD等待 

CMEMTHREAD等待就是等待被同步的內存對象。有一些對象支持查詢同時訪問,有些不支持。當一個查詢訪問一個對象時,其餘查詢就必須等待,這就是CMEMTHREAD等待

一般CMEMTHREAD等待不會很長時間。可是當內存出現問題後,cpu利用率和CMEMTHREAD等待都會變高,這是性能比較差的查詢引發的。

 

調度隊列

關於調度隊列最主要的視圖就是sys.dm_os_schedulers,視圖主要的二個指標之一是每一個調度器有幾個task,和運行隊列的長度。可運行隊列內都是等待cpu時鐘的task,其餘的taskcurrent_tasks_count內,都處於sleep或者在等待其餘資源。

SELECT scheduler_id ,

current_tasks_count ,

runnable_tasks_count

FROM sys.dm_os_schedulers

WHERE scheduler_id < 255

這些值沒有固定的閥值,只能經過性能基線來對比。固然這些值都是越低越好。若是可運行隊列越長那麼,signal time 的時間也就越長,就意味着可能cpu不足。

上面的sql過濾掉了一些 scheduler 由於其餘的是backupdac等調度器。

 

cpu密集型查詢

關於cpu密集型查詢,有2個性能視圖,sys.dm_exec_query_statssys.dm_exec_sql_textsys.dm_exec_query_stats統計了每一個查詢計劃的各種信息。如*_worker_timecpu花費的時間。*_elapsed_time:總共運行的時間。

下面的sql統計了前10個最費時間的查詢:

SELECT TOP ( 10 )

SUBSTRING(ST.text, ( QS.statement_start_offset / 2 ) + 1,

( ( CASE statement_end_offset

WHEN -1 THEN DATALENGTH(st.text)

ELSE QS.statement_end_offset

END - QS.statement_start_offset ) / 2 ) + 1)

AS statement_text ,

execution_count ,

total_worker_time / 1000 AS total_worker_time_ms ,

total_worker_time / 1000 ) / execution_count

AS avg_worker_time_ms ,

total_logical_reads ,

total_logical_reads / execution_count AS avg_logical_reads ,

total_elapsed_time / 1000 AS total_elapsed_time_ms ,

total_elapsed_time / 1000 ) / execution_count

AS avg_elapsed_time_ms ,

qp.query_plan

FROM sys.dm_exec_query_stats qs

CROSS APPLY sys.dm_exec_sql_text(qs.sql_handlest

CROSS APPLY sys.dm_exec_query_plan(qs.plan_handleqp

ORDER BY total_worker_time DESC

這個查詢並不會顯示全部的query,執行計劃是被保存在cache中的,當cache被淘汰,由於dbcc命令沒清理,數據庫狀態發生變化,數據庫配置發生變化等等,都會引發cache丟失的狀況。有一些查詢使用了recompile標示或者提示那就永遠不會被保留在cache中。

若是你要全局的分析執行計劃,請使用sql跟蹤,而不要事情清空緩存,特別是在生產庫中,緩存一旦被清空在一點時間內,講嚴重影響性能。

 

CPU使用率的建立幾種情況

無論在服務器硬件配置和技術上面花了多大的成本,總有怎麼一些查詢會致使服務器的資源滿負荷運行。每一個sql被執行的時候,sql server優化器終會找一個儘可能高效的方式來獲取數據。若是當一個查詢miss index或者忽略了合適的索引,那麼優化器就沒法生存一個真正高效的執行計劃。若是優化器相關的信息是不許確的,那麼優化器生存的執行計劃也是不許備的,由於關於成本的計算也是不許確的。另一種情況就是優化器生存的結果對一個查詢是優化的,可是對其餘查詢並不優化。由於不合適的參數探測致使了這個問題。

miss index

miss index 是照成大量cpuio使用的情況之一,也是最常發生的情況。當前的索引並不能知足查詢的時候,優化器會試圖是用表掃描來完成,這樣就照成了大量的非必須的數據參與到預算中,會照成cpuio的極大浪費。那麼咱們就以 adventureworks2008 數據庫做爲例子

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.SalesOrderID = sod.SalesOrderID

INNER JOIN Production.Product AS p ON sod.ProductID = p.ProductID

INNER JOIN Sales.Customer AS c ON soh.CustomerID = c.CustomerID

INNER JOIN Person.Person AS per

ON c.PersonID = per.BusinessEntityID

WHERE LineTotal > 25000

這個查詢在salesorderdetail使用了表掃描,由於並無關於linetotal列的索引

SQL Server parse and compile time:

CPU time = 0 ms, elapsed time = 0 ms.

SQL Server Execution Times:

CPU time = 452 ms, elapsed time = 458 ms.

雖然返回24行只用了半秒的時間可是仍是不夠優化。那麼咱們就在linetotal建一個索引

CREATE NONCLUSTERED INDEX idx_SalesOrderDetail_LineTotal

ON Sales.SalesOrderDetail (LineTotal)

那麼咱們繼續運行上面的sql

SQL Server parse and compile time:

CPU time = 0 ms, elapsed time = 0 ms.

SQL Server Execution Times:

CPU time = 0 ms, elapsed time = 8 ms.

結果有很大的不通,經過這個簡單的例子說明cpu的壓力有可能且很大的可能都是miss index 照成的。

 

統計數據丟失

優化器會經過統計信息估計每一個查詢操做的基數。經過估計行數,操做的花費。操做的花費決定了整個計劃的花費。若是統計信息不許確,那麼優化器的成本計算也就不許確,這樣就會致使優化器誤判,估計的花費是低的可是並不必定實際的花費也是低的。一般統計值不許確是比實際值要小,一旦小,那麼優化器就會選擇比較適合較小數量的操做符如nest loopkey lookup,可是實際的數據量很大,這樣就會對查詢照成嚴重的影響。有一個方法查看統計值是否丟失,就是在ssms中運行實際的查詢計劃,而且對比估計值和實際值的差距,若是差距很大那麼就是統計數據丟失了,須要及時更新統計值。固然能夠經過 update statistics 更新統計值,詳細的用法能夠參見聯機文檔。

若是是統計值過時的問題照成的那麼有一下3個方法:

1.把數據庫設置爲自動更新統計值。

2.若是自動更新統計信息無效,那麼有多是索引創建的時候有不計算統計值的標記。

3.建立一個腳本定時更新統計值。

 

SARG謂詞

就是不要再表的字段上使用函數或者計算,由於你一用,就沒辦法使用索引了。不能使用索引,顯而易見cpu高了,io堵塞了。

 

隱式類型轉化

不少人都認爲隱式轉化沒什麼關係,並不會給性能帶來多大的衝擊。一個過濾若是類型不一樣那麼sql server 是沒法比較的,這時候就要隱式轉化了,隱式轉化的時候都是從低的優先級轉化到高的優先級,好比若是一個是varchar一個是nvarchar那麼就會把varchar隱式轉化成nvarchar。問題就來了若是一個表列是varchar可是過濾的條件是nvarchar,那麼就會隱式轉化把varchar轉化成爲nvarchar那麼就會發生非SARG謂詞,沒法使用索引查找了。下面有個例子:

SELECT p.FirstName ,

p.LastName ,

c.AccountNumber

FROM Sales.Customer AS c

INNER JOIN Person.Person AS p ON c.PersonID = p.BusinessEntityID

WHERE AccountNumber = N'AW00029594'

固然 accountnumber 上是有索引的

就變成索引掃描了,我使用2008r2測試,結果不是索引掃描。可是當我把accountnumber 禁用掉以後,盡然和書上發的執行計劃是同樣的了,讓我深深的懷疑,是否是做者在寫書的時候,把accountnumber 禁用了而沒發現呢?我在網上查了寫資料,發現了在sql server 2000下的測試語句ok,在2000 下面是會照成索引掃描。因此你們若是用2008r2的就不須要太擔憂這個問題。若是在其餘版本真的遇到這個問題那麼如何解決呢?那麼就把類型轉化放在常量這一端。或者直接修改表的數據類型。

我把2000的測試語句發出來:

DECLARE @CustID NCHAR(5)

SET @CustID = N'FOLKO'

SELECT CompanyName FROM NorthWind.dbo.Customers WHERE CustomerID = @CustID

這裏要注意由於 customers 表的結構是 nchar的因此咱們在測試的時候先要修改掉這個數據類型,改成charnorthwind裏面有外鍵要通通刪掉,主鍵須要重建。

說到這裏,我就和書的做者聯繫了,根據他給的結論,和測試結果

-- Windows Collation will get a Seek
CREATE TABLE #T (col1 varchar(10) COLLATE Latin1_General_CI_AS PRIMARY KEY);
SELECT * 
FROM #T 
WHERE col1 = N'q'

-- SQL Collation will get a Scan
CREATE TABLE #T2 (col1 varchar(10) COLLATE SQL_Latin1_General_CP1_CI_AI PRIMARY KEY);
SELECT * 
FROM #T2 
WHERE col1 = N'q'

-- Your Collation will get a Seek
CREATE TABLE #T3 (col1 varchar(10) COLLATE Chinese_PRC_CI_AS PRIMARY KEY);
SELECT * 
FROM #T3 
WHERE col1 = N'q'

DROP TABLE #T
DROP TABLE #T2
DROP TABLE #T3

當你用SQL Server 的排序規則那麼就是掃描若是用windows 的排序規則那麼就是查詢。

上面就是他發過來的sample

 

參數探測器

sql server爲存儲過程,函數或者參數化查詢建立執行計劃的時候,會探測參數,並結合統計數據計算花費選擇較好的執行計劃。參數探測器只會在編譯或者重編譯的時候發生,那麼這裏就有個問題若是當建立執行計劃的時候該參數的值是非典型的,那麼就極可能並不適用於之後傳過來的參數。初始化編譯的時候,只有輸入的參數會被探測,本地變量是不會被探測的。若是一個語句在一個batch 中被重編譯那麼參數和變量都會被探測。

下面是一個運行在Adventureworks數據庫的例子最大日期是2011-7-8 最小日期是2004-8-7.

CREATE PROCEDURE user_GetCustomerShipDates

(

@ShipDateStart DATETIME ,

@ShipDateEnd DATETIME

)

AS

SELECT CustomerID ,

SalesOrderNumber

FROM Sales.SalesOrderHeader

WHERE ShipDate BETWEEN @ShipDateStart AND @ShipDateEnd

GO

會對shipdate進行過濾那麼就在shipdate設置一個索引

CREATE NONCLUSTERED INDEX IDX_ShipDate_ASC

ON Sales.SalesOrderHeader (ShipDate)

GO

接下來會運行2次這個存儲過程第一次誇多年的,第二次就誇幾天。並查看實際的執行計劃

DBCC FREEPROCCACHE

EXEC user_GetCustomerShipDates '2001/07/08', '2004/01/01'

EXEC user_GetCustomerShipDates '2001/07/10', '2001/07/20'

查詢結果2個都用了掃描

       |--Filter(WHERE:([AdventureWorks].[Sales].[SalesOrderHeader].[ShipDate]>=[@ShipDateStart] AND [AdventureWorks].[Sales].[SalesOrderHeader].[ShipDate]<=[@ShipDateEnd]))
            |--Compute Scalar(DEFINE:([AdventureWorks].[Sales].[SalesOrderHeader].[SalesOrderNumber]=[AdventureWorks].[Sales].[SalesOrderHeader].[SalesOrderNumber]))
                 |--Compute Scalar(DEFINE:([AdventureWorks].[Sales].[SalesOrderHeader].[SalesOrderNumber]=isnull(N'SO'+CONVERT(nvarchar(23),[AdventureWorks].[Sales].[SalesOrderHeader].[SalesOrderID],0),N'*** ERROR ***')))
                      |--Table Scan(OBJECT:([AdventureWorks].[Sales].[SalesOrderHeader]))

這個是個人結果和書上的不同。那麼爲何爲產生表掃描不是索引查找呢,由於第一個查詢在編譯的時候優化器任務用表掃描比較合適。可是到第二句的時候,雖然是不合適,可是已經有執行計劃存儲在了內存裏面,sql server 就直接拿來用了,就照成了這個問題。開 SET STATISTICS IO on

'SalesOrderHeader'。掃描計數1,邏輯讀取700 次,物理讀取0 次,預讀0 次,lob 邏輯讀取0 次,lob 物理讀取0 次,lob 預讀0 次。

那麼咱們把2個存儲過程倒過來:

DBCC FREEPROCCACHE

EXEC user_GetCustomerShipDates '2001/07/10', '2001/07/20'

EXEC user_GetCustomerShipDates '2001/07/08', '2004/01/01'

狀況就徹底不同了

       |--Compute Scalar(DEFINE:([AdventureWorks].[Sales].[SalesOrderHeader].[SalesOrderNumber]=[AdventureWorks].[Sales].[SalesOrderHeader].[SalesOrderNumber]))
            |--Nested Loops(Inner Join, OUTER REFERENCES:([Bmk1000], [Expr1004]) WITH UNORDERED PREFETCH)
                 |--Index Seek(OBJECT:([AdventureWorks].[Sales].[SalesOrderHeader].[IDX_ShipDate_ASC]), SEEK:([AdventureWorks].[Sales].[SalesOrderHeader].[ShipDate] >= [@ShipDateStart] AND [AdventureWorks].[Sales].[SalesOrderHeader].[ShipDate] <= [@ShipDateEnd]) ORDERED FORWARD)
                 |--Compute Scalar(DEFINE:([AdventureWorks].[Sales].[SalesOrderHeader].[SalesOrderNumber]=isnull(N'SO'+CONVERT(nvarchar(23),[AdventureWorks].[Sales].[SalesOrderHeader].[SalesOrderID],0),N'*** ERROR ***')))
                      |--RID Lookup(OBJECT:([AdventureWorks].[Sales].[SalesOrderHeader]), SEEK:([Bmk1000]=[Bmk1000]) LOOKUP ORDERED FORWARD)

果斷使用了索引查找,可是對第二句來講索引查找不必定是好事情,由於他要掃描的行太多,若是假定如今樹是3層,那麼讀一個頁須要3次你想一想。

'SalesOrderHeader'。掃描計數1,邏輯讀取17155 次,物理讀取0 次,預讀0 次,lob 邏輯讀取0 次,lob 物理讀取0 次,lob 預讀0 次。

比較一下誇多年的那個存儲過程的邏輯讀。

一般keylookup只適合較少的數據一般是整表的1%,固然不是絕對的。

跟蹤標記4136

SQL Server 2008 引入了一個新的跟蹤標記 4316,使用了這個跟蹤標記以後sql server 會關掉參數探測功能,這個功能在sql server 2008 sp2 cu7 ,sql server 2--8 r2 cu2,sql server 2005 sp3 cu9 中才加入。先前討論過了若是開了參數探測,一個存儲過程若是第一次編譯的時候估計值偏小,或者偏大,都會對接下來使用這個存儲過程產生影響。當參數探測器被停用的時候 4316 跟蹤是如何處理的呢,舉個例子這裏又一個列 X 有以下的值1,2,3,3,3,3,3,4,5,5,那麼他的估計值就是2這個哪來的?就是全部數據的平均估計值。全部的計劃都會被這個值優化。若是開了這個選項那麼會給不少存儲過程優化帶來好處。

這邊有篇關於4316的文章比較簡單可是很到位:http://blogs.msdn.com/b/axperf/archive/2010/05/07/important-sql-server-change-parameter-sniffing-and-plan-caching.aspx

使用 OPTIMIZE FOR 提示

到了sql server 2005 之後你可使用OPTIMIZE FOR 來優化查詢

CREATE PROCEDURE user_GetCustomerShipDates

(

@ShipDateStart DATETIME ,

@ShipDateEnd DATETIME

)

AS

SELECT CustomerID ,

SalesOrderNumber

FROM Sales.SalesOrderHeader

WHERE ShipDate BETWEEN @ShipDateStart AND @ShipDateEnd

OPTION ( OPTIMIZE FOR ( @ShipDateStart = '2001/07/08',

@ShipDateEnd = '2004/01/01' ) )

GO

使用了OPTIMIZE FOR 提示那麼sql server 就會按提示的信息來編譯,固然若是提示的值不理想那麼也會產生問題。

SQL Server 2008 中引入了一個新的提示 OPTIMIZE FOR UNKNOWN,那麼sql server 就不會再用參數探測的功能,它的功效和4316相同,因此這個方法是比較可取的由於畢竟參數探測仍是一個比較好的東西。

重編譯選項

重編譯也是解決參數探測的一個方法,可是問題就是執行計劃不會被保存在內存中,可是就有一個問題存儲過程的執行的花費就會變高。

CREATE PROCEDURE user_GetCustomerShipDates

(

@ShipDateStart DATETIME ,

@ShipDateEnd DATETIME

)

WITH RECOMPILE

AS

SELECT CustomerID ,

SalesOrderNumber

FROM Sales.SalesOrderHeader

WHERE ShipDate BETWEEN @ShipDateStart AND @ShipDateEnd

GO

若是存儲過程當中只須要一部分從新編譯,那麼就可使用OPTION(RECOMPILE)選項放到查詢中便可,相比重編譯整個存儲過程,這樣會好些。

CREATE PROCEDURE user_GetCustomerShipDates

(

@ShipDateStart DATETIME ,

@ShipDateEnd DATETIME

)

AS

SELECT CustomerID ,

SalesOrderNumber

FROM Sales.SalesOrderHeader

WHERE ShipDate BETWEEN @ShipDateStart AND @ShipDateEnd

OPTION ( RECOMPILE )

GO

 

ad hoc 非參數化查詢

Ad hoc查詢語句發送到sql server 的時候優化器仍是會從cache查找合適的執行計劃。ad hoc 查詢會讓全部的語句都生產一遍執行計劃,這樣會照成資源浪費特別是CPU

SELECT soh.SalesOrderNumber ,

sod.ProductID

FROM Sales.SalesOrderHeader AS soh

INNER JOIN Sales.SalesOrderDetail AS sod

ON soh.SalesOrderID = sod.SalesOrderID

WHERE soh.SalesOrderNumber = 'SO43662'

SELECT soh.SalesOrderNumber ,

sod.ProductID

FROM Sales.SalesOrderHeader AS soh

INNER JOIN Sales.SalesOrderDetail AS sod

ON soh.SalesOrderID = sod.SalesOrderID

WHERE soh.SalesOrderNumber = 'SO58928'

SELECT soh.SalesOrderNumber ,

sod.ProductID

FROM Sales.SalesOrderHeader AS soh

INNER JOIN Sales.SalesOrderDetail AS sod

ON soh.SalesOrderID = sod.SalesOrderID

WHERE soh.SalesOrderNumber = 'SO70907'

很不幸,這三個語句原本是應該能夠用同一個執行計劃的。如今由於ad hoc 用不了了。若是是簡單的查詢那麼sql server 會使用簡單參數化來重用執行計劃。可是上面的例子太複雜了因此沒辦法。那就會有2個問題

1.執行計劃緩存充滿了單用戶的計劃,不能被重用。浪費內存空間。

2.執行計劃由於可用因此老是要編譯新的計劃,致使cpu時鐘浪費。

能夠用perfmon來監視編譯重編譯的量

 SQLServer: SQL Statistics: SQL Compilations/Sec

 SQLServer: SQL Statistics: Auto-Param Attempts/Sec

 SQLServer: SQL Statistics: Failed Auto-Param/Sec

若是真的是非參數化照成的問題,那麼又不少方法去調整,最好的方式是修改源代碼。若是不行那麼只能設置sql server 來調整

修改源代碼

關於修改源代碼就不討論了,直接給demo本身看。

cmd.CommandType = CommandType.Text;

cmd.CommandText = @"SELECT soh.SalesOrderNumber,

sod.ProductID

FROM Sales.SalesOrderHeader AS soh

INNER JOIN Sales.SalesOrderDetail AS sod

ON soh.SalesOrderID = sod.SalesOrderID

WHERE soh.SalesOrderNumber = '" + txtSalesOrderNo.Text + "'";

dtrSalesOrders = cmd.ExecuteReader();

dtrSalesOrders.Close();

cmd.CommandType = CommandType.Text;

cmd.CommandText = @"SELECT soh.SalesOrderNumber,

sod.ProductID

FROM Sales.SalesOrderHeader AS soh

INNER JOIN Sales.SalesOrderDetail AS sod

ON soh.SalesOrderID = sod.SalesOrderID

WHERE soh.SalesOrderNumber = @SalesOrderNo";

cmd.Parameters.Add("@SalesOrderNo", SqlDbType.NVarChar, 50);

cmd.Parameters["@SalesOrderNo"].Value = txtSalesOrderNo.Text;

dtrSalesOrders = cmd.ExecuteReader();

強制性參數化

關於強制參數化,能夠設置數據庫選項

ALTER DATABASE AdventureWorks SET PARAMETERIZATION FORCED

若是使用強制參數化那麼上面咱們提過的3sql的執行計劃就變成一個了。可使用以下sql查詢

SELECT b.text,c.* FROM sys.dm_exec_query_stats   a

      CROSS APPLY sys.dm_exec_sql_text(a.sql_handle) b

      CROSS APPLY  sys.dm_exec_query_plan(a.plan_handle) c  

使用強制參數化很很差,就會使得全部的sql都使用同一個查詢計劃,無論好壞,有點和參數探測器的問題相似了。

Optimize for ad hoc workloads

這是一個數據庫服務配置項,配置了以後當ad hoc第一次運行的時候sql server 會產生個子查詢計劃不能用,當第二次執行的時候產生一個執行計劃。能夠有效的減小內存壓力。

EXEC sp_configure 'show advanced options',1

RECONFIGURE

EXEC sp_configure 'optimize for ad hoc workloads',1

RECONFIGURE

 

不合適的併發查詢

當查詢在不一樣的線程,每一個線程在不一樣的調度器下運行,就能夠理解爲併發查詢。

當一個查詢被提交到sql server 優化器,優化器開始估算花費,若是花費比cost threshold for parallelism 要大,那麼優化器會考慮使用併發。max degree of parallelism 用來限制查詢的最大併發數若是查詢中使用了maxdop提示的話那麼最大併發數則爲提示的值。併發查詢經過把數據水平分區到各個不一樣的邏輯cpu,經過多個處理器內核執行相同的操做來減小查詢的時間。這個對於dw或者報表數據庫是頗有用的由於數據量很大,並且併發請求比較少。因此可以充分的利用硬件資源,而且減小執行的時間。對於併發的負載仍是又一些要素,並非指餘下的設備資源可否應付併發負載帶來的大內存分配和磁盤io的問題。併發查詢使用的好會給服務器的總體性能帶來很大的提高,可是併發負載對oltp系統來講是很是不利的,oltp是又不少小的事務組成,併發量比較大,若是oltp上有併發負載,佔據了較長時間的cpu,那麼其餘事務就會等待併發的完成,致使查詢假死在那邊。

對於併發的配置參數有2cost threshold for parallelism max degree of parallelism 第一個是啓用併發查詢的閥值,第二個是最大併發數。當發生不合適的併發的時候,建議的解決方法是調整max degree of parallelism,減小1/2,或者減小1/4或者直接設置爲1。固然這個是不理想的解決方案,最理想的解決方案是設置2個配置參數,到一個比較合理的值。

cost threshold for parallelism 

cost threshold for parallelism 是一個啓用併發的閥值,查過了就啓用併發,沒超過就不啓用。cost threshold for parallelism 的默認值是5秒,可是對於大數據庫5秒是一個比較小的值,所以設置cost threshold for parallelism 閥值很重要

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED ;

WITH XMLNAMESPACES

(DEFAULT 'http://schemas.microsoft.com/sqlserver/2004/07/showplan')

SELECT query_plan AS CompleteQueryPlan ,

n.value('(@StatementText)[1]', 'VARCHAR(4000)') AS StatementText ,

n.value('(@StatementOptmLevel)[1]', 'VARCHAR(25)')

AS StatementOptimizationLevel ,

n.value('(@StatementSubTreeCost)[1]', 'VARCHAR(128)')

AS StatementSubTreeCost ,

n.query('.') AS ParallelSubTreeXML ,

ecp.usecounts ,

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="Parallelism"]') = 1

因此經過以上查詢,分析類似的查詢。以最小化cpuio競爭爲目標設置cost threshold for parallelism

max degree of parallelism

sql server 併發查詢的併發度有如下3點:

1.可用的處理器數量

2.max degree of parallelism

3.MAXDOP查詢提示

若是你的服務器如今出現了併發問題那麼修改閥值和最大併發度是解決這個問的最快速的方法。

網上有種說法就是直接把max degree of parallelism設爲1,對於oltp系統的特性是可能性的,可是仍是以爲你這樣設置以後就不能使用併發了,感受會減小性能。

分析CXPACKETwait eventCXPACKET只是一種症狀,並無真正的發生問題。查看sys.dm_os_waiting_tasks中其餘的wait event能夠更好的得出合適的 max degree of parallelism。若是相關的等待事件是PAGEIOLATCH_SH,併發正在等待io讀取,減小max degree of parallelism 並不能解決根本問題,它只會減少被使用的工做任務,減小CXPACKET累計等待時間。可是也可能會減小額外的io,給你提示io性能的空間。

併發查詢也須要考慮到內存的結構體系,在NUMA結構下,最大併發度設置在一個NUMA節點的可用常常。這樣node之間就不會產生交互,由於node間的共享內存操做代價很高。在SMP結構中,多個處理器內核都在單個芯片上共享二級緩存,這樣很容易照成內存命中率降低,可是好處是在併發查詢下高併發的性能表現很好,固然max degree of parallelism 也要根據硬件設備的能力作適當的調節。在sql server 2008 以上的版本還可使用資源管理器來限制。

超線程和併發查詢

超線程是Intel一個技術,爲了提升併發操做,就設計了2個邏輯內核對於1個物理內核。就是說不想之前一個調度器一個物理內核,如今2個內核,而且能夠同時使用。固然咱們關心的是性能,那麼sql server 有沒有使用超線程,會給sql server 帶來什麼影響。

對於olapdss系統併發查詢是又很大好處的,可是當開了超線程的時候性能就變差了。可是超線程對oltp沒什麼影響,對於oltp來講超線程在增長併發度是又好處的。對於早期的超線程由於會帶來不少問題因此dba都是在bios中關閉超線程的。近幾年sql server 2008 發佈了建議關閉超線程特別是olap/dw/dss系統。超線程最大的問題是超線程會共享內置的cache,照成命中率降低。如今不少問題都解決了,windows 2003 就能認識物理內核和邏輯內核,而且給予不一樣的工做量。如今的處理器緩存變大不容易發生。事實上對於當前的處理器結構,特別是intel nehalem開超線程是有好處的,除非是有明確的理由。因此在決定是否使用超線程的時候最好先作一下測試。

診斷不合適的併發查詢

最好診斷的方法是查看wait統計信息和latch統計信息,當執行併發的時候出現瓶頸,CXPACKET等待就會變的很高。當併發查詢等待交換迭代器到另一個工做任務的時候就會發生等待。一般這裏也會有一些相關的其餘等待,來協助工做,由於大量的併發查詢,CXPACKET的等待會比根本緣由蓋過去。最好的方法是分隔在troubleshooting各個相關的等待時間。由於併發查詢會影響全局的性能問題。CXPACKET頗有可能只是一個症狀不少問題都會引發CXPACKET偏高。當io不能維持併發查詢的需求,關鍵的等待多是IO_COMPLETION,ASYNC_IO_COMPLETION,PAGEIOLATCH_*,不能擴展io性能。可是減少併發度,任然會發生io性能瓶頸的情況,那麼就要提從全局的系統性能。若是CXPACKE相關的等待是LATCH_*,SOS_SCHEDULER_YIELD,那麼頗有多是併發的問題,深刻latch驗證是併發的問題。sys.dm_os_latch_stats包含一些特殊的latch等待,如ACCESS_METHODS_DATASET_PARENT,LATCH_*,SOS_SCHEDULER_YIELD等待都比較高,那麼減小併發度就可能解決問題。

解決併發問題

先前已經討論過,對於大的,長運行時間的查詢使用併發頗有好處。不合適的併發主要問題是負載類型是混合的。不少庫本質上是oltp的可是由於sql比較複雜超過了cost threshold for parallelism。因此試圖提高一下cpu性能。若是診斷到了併發存在問題,若是沒有被調整過,那麼頗有可能由於索引丟失或者不合適的索引形成問題,若是調整完以後仍是這樣那麼就用先前提到的2個系統配置參數,來全局的管理數據庫併發。

TokenAndPermUserStore

TokenAndPermUserStore2005的時候被引進來優化關於權限驗證,怎麼TokenAndPermUserStore是怎麼工做的呢?這裏有一個簡單的例子說明TokenAndPermUserStore的工做狀況。例子當你執行的時候select * from t1 join t2 join t3,那麼sql sever 就會對權限進行驗證,驗證後會緩存在TokenAndPermUserStore以避免之後重複驗證。可是這個會引發性能問題,特別是較早版本的sql server 2005,由於這個cache的內存限制太高性能問題的表現爲cpu使用率比較高,cmemthread等待比較嚴重。微軟已經給出了一個解決方案http://support.microsoft.com/kb/927396/一般問題發生在非awe內存分配的sql server上(特別是64b的服務器),不少動態的或者 adhoc查詢,數據庫用戶過多。你可使用以下sql查詢TokenAndPermUserStore使用量:

SELECT SUM(single_pages_kb + multi_pages_kb) / 1024.0 AS CacheSizeMB

FROM sys.dm_os_memory_clerks

WHERE [name] = 'TokenAndPermUserStore'

若是cache一直增加,而且伴隨着cmemthread等待,那麼頗有可能致使高cpu使用率,若是使用sql server2005低於sp2補丁,那麼第一時間就是打上補丁。嫌少動態sqladhoc來減小發生問題的機率。

短時間修復

使用sysadmin角色,由於sysadminsql server 最大的權限,不須要作權限檢查。那麼也就不會產生cache

按期清理cacheDBCC FREESYSTEMCACHE ('TokenAndPermUserStore')

sql 2005 sp2 以上版本使用 trace flage 4618,4610來限制cache中的條目數量,當4618開啓,cache中只能有1024cache,當2trace flag 都開啓那麼又8192個條目。這個限制會影響其餘cache,所以只能臨時使用。sql server 2005 sp3之後有個新的trace flag 4612,能夠設置客戶端的配額詳細看:(http://support.microsoft.com/kb/959823)

sql2008的配置項

sql server 2008 對於TokenAndPermUserStore2個配置項,access check cache quotaaccess check cache bucket count,若是問題很明顯的發生,那麼就減小這2個值的大小,其實並不建議修改默認值,除非又微軟客服支持。

 

總結

troubleshooting是一個分析問題的過程,我上一篇文章也說了,是一個根據統計的信息,分析問題的過程。所以須要瞭解數據庫內核,內部運行的結構才能更好的進行調優。調優第一步的信息每每都是來至於perfmon,和動態性能視圖,最後纔是sqltrace,爲啥,由於sqltrace最浪費時間,會有滯後性,因此已經滯後了還不如放到最後運行。

 

參考資料:

 Implicit data conversations
•http://sqlblog.com/blogs/jonathan_kehayias/archive/2010/01/08/findingimplicit-column-conversions-in-the-plan-cache.aspx
 Query tuning
• http://www.straightpathsql.com/presentations/ucandoit/
• http://www.simple-talk.com/sql/performance/simple-query-tuning-with-statistics-io-and-execution-plans/

•http://www.simple-talk.com/sql/t-sql-programming/13-things-youshould-know-about-statistics-and-the-query-optimizer/
• http://www.simple-talk.com/author/gail-shaw/
 Estimated vs. actual row counts
• http://sqlinthewild.co.za/index.php/2009/09/22/estimated-rows-actual-rows-and-execution-count/
 Cost threshold for parallelism
• http://sqlblog.com/blogs/jonathan_kehayias/archive/2010/01/26/21172.aspx
• Max degree of parallelism
• http://msdn.microsoft.com/en-us/library/ms181007.aspx
 Query hints
• http://msdn.microsoft.com/en-us/library/ms181714.aspx
 Guidelines for modifying MAXDOP
• http://support.microsoft.com/kb/329204
 Limiting MAXDOP with the Resource Governor
•http://www.sqlmag.com/blog/sql-server-questions-answered-28/database-administration/controlling-maxdop-executing-queries-140163
 Parallelism/MAXDOP configuration
• http://msdn.microsoft.com/en-us/library/ms178065.aspx
• http://msdn.microsoft.com/en-us/library/ms188611.aspx
• http://blogs.msdn.com/b/joesack/archive/2009/03/18/should-you-worryabout-sos-scheduler-yield.aspx

 SQLOS architecture
• http://blogs.msdn.com/b/sqlosteam/archive/2010/06/23/sqlos-resources.aspx
•http://sqlblogcasts.com/blogs/sqlworkshops/archive/2007/11/25/findingoptimal-number-of-cpus-for-a-given-long-running-cpu-intensive-dss-olaplike-queries-workload.aspx
 System Monitor CPU counters
• http://msdn.microsoft.com/en-us/library/ms178072.aspx
 DMV usage for CPU usage from ring buffers
•http://troubleshootingsql.com/2009/12/30/how-to-find-out-the-cpuusage-information-for-the-sql-server-process-using-ring-buffers/
• http://msdn.microsoft.com/en-us/library/ms175048(SQL.90).aspx
• http://technet.microsoft.com/en-us/library/cc966540.aspx
 Forced parameterization
• http://technet.microsoft.com/en-us/library/ms175037(SQL.90).aspx
 Fixing TokenAndPermUserStore problems Identification and overview
• http://support.microsoft.com/kb/927396
 Access check result cache
• http://support.microsoft.com/kb/955644
• http://msdn.microsoft.com/en-us/library/cc645588.aspx
• Purging the cache whenever it reaches a certain size
• http://blogs.msdn.com/chrissk/archive/2008/06/19/script-to-purgetokenandpermuserstore.aspx

 SQL Server 2008 sp_configure options• http://support.microsoft.com/kb/955644/en-us• Hot-fixes associated with this problem• http://support.microsoft.com/kb/959823

相關文章
相關標籤/搜索