標籤:
原文地址:http://www.cnblogs.com/lyhabc/p/3367274.html
看這篇文章之前,閣下可以先看一下下面的文章
SQLSERVER獨特的任務調度演算法"SQLOS"
SQL Server SQLOS 的任務調度[轉]
翻譯自:
http://rusanu.com/2013/08/01/understanding-how-sql-server-executes-a-query/
http://www.codeproject.com/Articles/630346/Understanding-how-SQL-Server-executes-a-query
不知道是哪篇文章抄哪篇文章的 ,不管他了,我也偷他們的文章,嘎嘎嘎嘎嘎。。。
我將會用盡本人的所有功力並且結合研究SQLSERVER以來的知識去翻譯這篇文章
希望大家多多支援o(∩_∩)o,其實我也是站在巨人的肩膀上的,呵呵~
特別說明:為了節省篇幅,文中會對原文有刪減,刪減的部分都是一些不重要的部分
在這裡感謝有道詞典(我不是賣廣告啊!!!)
正式開始
串連方式和請求
如果你是一個開發人員,並且你的程式使用SQLSERVER來做資料庫的話
你會想知道當你用你的程式執行一個查詢的時候實際發生了什麼事情
我希望這篇文章能夠幫你寫出更好的資料庫應用程式和幫你更深入瞭解遇到的資料庫效能問題
SQLSERVER是一個C/S模型的平台。唯一和資料庫互動的方式只有發送包含資料庫命令的請求到資料庫伺服器端。
用戶端和資料庫通訊的協議使用一種叫做TDS的協議(Tabular Data Sream)
園子裡的文章:
表格式資料流協議TDS
TDS協議解析(轉載)
如果你用微軟的Network Monitor工具來抓取SQL Server和用戶端之間的網路包
你會看到使用的是TDS協議
在Description那一列
TDS:Response,Version=7.1......
TDS:SQLBatch,Version=7.1.......
那四個SSL串連是用戶端登入SQLSERVER前做的加密串連(這裡不管你有沒有用SSL加密資料傳輸,SQLSERVER都會在登入前加密
使用者發過來的使用者名稱和密碼,而登入了之後才使用您配置的SSL認證來加密用戶端和SQLSERVER往來的資料)
SQLSERVER都會加密用戶端發過來的使用者名稱和密碼(使用SQL驗證不是使用Windows驗證)
大家可以留意一下SQL ERRORLOG裡在SQLSERVER啟動的時候的日誌
會看到一句:A self-generated certificate was sccessfully loaded for encryption
預設情況下SQL Server會自動產生一個認證並使用這個認證來對用戶端登入SQLSERVER的時候的串連做SSL加密登入了SQLSERVER之後,就不會對串連/所傳輸的資料做加密了
而且SQL Server自動產生的認證。每次SQL Server啟動時,它自動產生的認證都是不一樣的
MSDN是這樣描述的:Tabular Data Stream協議,應用程式能夠使用下面的幾種已經實現了TDS協議的驅動程式裡的其中一種
驅動程式來串連資料庫,包括:
the CLR managed SqlClient
OleDB
ODBC
JDBC
PHP Driver for SQL Server
開源的 FreeTDS 實現
當你的應用程式命令資料庫如何去做的時候會通過TDS協議向資料庫發送一個請求
發送的請求本身能攜帶下面幾種格式的資訊
(1)批處理請求
這種請求類型只會包含一個需要執行的批處理TSQL文本。這種類型的請求不能帶有參數,不過,TSQL批處理指令碼裡
能包含本地變數的定義。這種類型的請求一般都是使用SQLCLIENT驅動程式發送的,
當你使用SqlCommand 對象調用下面語句的任何一個的時候,並且沒有傳入任何參數
SqlCommand.ExecuteReader()
SqlCommand.ExecuteNonQuery()
SqlCommand.ExecuteScalar()
SqlCommand.ExecuteXmlReader()
當你用SQL PROFILER監視你會看到一個:SQL:BatchStarting 事件類型
(2)遠端程序呼叫請求
這個請求類型包含帶有若干個參數的預存程序。
當你用SQL PROFILER監視你會看到一個:RPC:Starting 事件類型
(3) Bulk Load大容量裝載請求
大容量裝載請求是一種特別的使用bulk insert操作符的請求,
例如使用
BCP.EXE工具(我們常說的BCP命令)
bulk insert語句
IRowsetFastLoad OleDB 介面
C#裡面的SqlBulkcopy類
大容量裝載請求跟其他類型的請求是不同的,
因為請求通過TDS協議傳送到SQLSERVER的時候,還未傳送完畢,SQLSERVER就開始執行請求所要做的操作了
(一般來說,整個請求的資料包全部發送到SQLSERVER那裡,SQLSERVER認為是完整的資料包才開始執行請求)
但是大容量裝載請求不一樣,資料包裡包含有大量的資料,這些資料是附著在請求裡的,如果要把整個請求傳送完畢
SQLSERVER才開始執行請求,那不知道要等到何年何月了???
這樣允許SQLSERVER開始執行請求,並且開始消費掉資料流中所插入的資料
下面是比較久以前的一張圖片,大家可以參考一下,圖片內容對於現在的SQLSERVER不一定正確
串連模組(模組化)
任務(Tasks)和工作者(Workers)
在一個完整的TDS請求到達SQLSERVER資料庫引擎的時候,SQLSERVER會建立一個任務(task)去處理請求
想查詢當前SQLSERVER裡面的所有請求,可以使用sys.dm_exec_requests 這個DMV視圖
任務(Tasks)
上面提到的任務會被建立用來處理請求,一直到請求處理完畢為止。
例如:如果請求是一個批處理請求類型的請求,任務(Tasks)會執行整個SQL批處理,不會只負責執行SQL批處理裡的單獨一條SQL語句
在SQL批處理裡的單獨的一條SQL語句不會建立一個新的任務(Tasks)。
當然,在SQL批處理裡的單獨的一條SQL語句有可能會並存執行(通常使用MAXDOP,或Degree Of Parallelism)
在這種情況下,任務(Tasks)會再生新的子任務(sub-Tasks)去並存執行這個單獨的SQL語句。
如果請求返回了批處理所要的完整的結果集,並且結果集已經被用戶端從SQLSERVER的結果集緩衝裡取走
並且你在C#代碼裡dispose 了SqlDataReader,你會在sys.dm_os_tasks這個DMV視圖裡看到你的請求
所用的任務(Tasks)。
當一個新的請求到達SQLSERVER伺服器端的時候,並且這時候任務(Tasks)已經被建立出來去處理這個請求了
如果這時候任務(Tasks)處於掛起(PENDING)狀態,現階段SQLSERVER還未知道這個請求的實際內容,
那麼,被建立出來的任務必須首先去執行這個請求,並且資料庫引擎也要分配一個工作者(Worker)去處理這個請求
工作者(Workers)
工作者(Workers)是SQLSERVER線程池裡的一些線程,一些 工作者(Workers)/背景工作執行緒在SQLSERVER
初始化的時候就被建立出來,而另一些工作者(Workers)會根據需求而建立,當建立的數量達到max worker threads
這個配置值的時候就不能再建立了,顯示為0,他並不是說可以建立無限的工作者(Workers)
我的機器的配置是酷睿i3,雙核四線程,那麼,如果max worker threads配置為0
最大的工作者(Workers)數目可以達到256個
實際上,只有工作者(Workers)才真正執行SQL代碼。
工作者(Workers)每時每刻都等待那些已經傳送進去SQLSERVER的請求的任務(Tasks)
從被掛起(PENDING)狀態成為可以啟動並執行狀態,每個工作者(Workers)只會處理和執行一個任務(Tasks)
這時候,工作者(Workers)會一直處於工作狀態,並一直被佔用,直到他的工作完成為止(task finishes)
如果當前沒有可用的工作者(Workers)供給正在處於掛起狀態的任務(Tasks)使用的話,那麼這個任務(Tasks)
只能一直等待直到那些已經在執行/啟動並執行任務(Tasks)執行完畢,另外,工作者(Workers)在處理完一個
任務(Tasks)之後也會繼續處理下一個處於掛起狀態的任務(Tasks)。
對於一個SQL批處理請求,工作者(Workers)會處理那個攜帶著那個SQL批處理的任務(Tasks)
並且會執行SQL批處理裡面的每條SQL語句。
有人就會問了:一個SQL批處理裡的SQL語句不就是並存執行嗎?
(=> 請求request =>任務 task =>工作者 worker),一個批處理請求進來,多個工作者去處理這個批處理請求裡的每條SQL語句,
這顯然就是SQLSERVER的並發處理SQL語句嘛
很多人都會有這個想法,實際上是錯誤的,實際上這些SQL語句也是串列執行的,這些SQL語句的執行只能由
一個單獨的線程(工作者 worker)來執行,線程(工作者 worker)在執行完一個SQL語句之後才能執行下一個SQL語句,
當SQL批處理內部的SQL語句使用了並行提示MAXDOP>1來執行SQL語句 ,這會造成建立子任務(sub-tasks),
每個子任務(sub-tasks)也是通過上面所說的那個迴圈去執行的:任務建立出來之後會處於掛起狀態,
其他的(工作者 worker)必須去處理這個子任務(sub-tasks)
你會在sys.dm_os_workers這個DMV視圖裡看到SQLSERVER當前的工作者 worker列表和他們的目前狀態
解釋(Parsing)和編譯(Compilation)
一旦一個任務(task)開始執行一個請求,第一件要做的事情就是:去理解請求裡面的內容
在這一步,SQLSERVER的行為更像一個代碼解釋的虛擬機器(類似於JVM):在請求(request)裡面的TSQL代碼將會被逐一解釋
並且會產生一棵抽象文法樹去處理這個請求。整個批處理請求會被解釋和編譯,如果在這一步發生錯誤,
SQLSERVER會給出編譯/解釋錯誤的提示,這個請求也會被終止不會執行,任務(task)和工作者(worker)都會被釋放,
釋放出來的工作者(worker)會繼續處理下一個被掛起的任務(task)。
SQL語言和TSQL(SQLSERVER裡叫TSQL,ORACLE裡叫PLSQL)語言是一種高等的描述性語言
當一個SQL語句很複雜的時候,試想一下,一個SELECT 語句伴隨著多個JOIN
1 USE [GPOSDB] 2 GO 3 SELECT * FROM [dbo].[CT_Append] AS a 4 INNER JOIN 5 [dbo].[CT_FuelingData] AS b 6 ON a.[VC_A_CardNO]=b.[VC_FD_Cardno] 7 INNER JOIN 8 [dbo].[CT_Dis_FuelingData] AS d 9 ON a.[VC_A_CardNO]=d.[VC_FD_Cardno]10 INNER JOIN 11 [dbo].[CT_InhouseCard] AS e12 ON e.[VC_IC_CardNO]=d.[VC_FD_Cardno]13 INNER JOIN14 [dbo].[CT_OuterCard] AS f15 ON f.[VC_OC_CardNO]=a.[VC_A_CardNO]
編譯好的TSQL批處理不會產生可執行代碼(executable code,類似可執行檔二進位的exe檔案),
這裡更像本地CPU指令,甚至於類似C#的CLI指令或者JAVA的JVM bytecode
不過,這裡會產生用於訪問表資料的執行計畫(query plans),這些執行計畫描述了如何去訪問表和索引,
如何去搜尋和定位表裡面的行資料,如何根據SQL批處理裡的SQL語句去做資料操作。
例如:一個執行計畫會描述一種資料訪問路徑-》訪問在t表上的索引idx1,定位到關鍵字為‘k’的那行記錄,
最後返回a列和b列這兩列資料。
另外:開發人員通常都會犯一個普遍的錯誤
在一個TSQL語句裡寫很多的條件選擇,通常這些條件選擇都會用在帶有OR 的where子句裡
例如:[email protected] OR @parameter IS NULL
對於開發人員一定要避免這種情況。
這個時候,編譯一定要得出一種通用的執行計畫,無論任何參數代入到這個執行計畫裡都能得出最優的結果
在TSQL裡的參數化(Dynamic Search Conditions)
例如下面SQL語句:
1 SET STATISTICS PROFILE ON2 GO3 INSERT INTO [dbo].[SystemPara] ( [ParaValue], [Name], [Description] )4 VALUES ( ‘2‘, -- ParaValue - varchar(50)5 ‘3‘, -- Name - varchar(50)6 ‘4‘ -- Description - varchar(50)7 )
當你開啟SET STATISTICS PROFILE ON開關的時候,你會在Argument列和DefinedValues列看到
SQLSERVER會將輸入的值2,3,4賦值到Expr1004,Expr1005,Expr1006這三個變數裡
並做一些類型轉換,Expr1004=CONVERT_IMPLICIT(VARCHAR(50),[@1],0)
2這個值會代入都@1變數裡,然後通過類型轉換賦值給Expr1004
recordno這一列也是,通過getidentity((277576027),(14),null)函數獲得自增值
然後賦值給Expr1003
那麼,在SQLSERVER的執行計畫裡,大家可以想象成如下樣子
1 INSERT INTO [dbo].[SystemPara] ([RecordNo], [ParaValue], [Name], [Description] )2 VALUES(Expr1003,Expr1004,Expr1005,Expr1006)
將實際的值先賦值給@1,@2,@3,@4 再通過類型轉換賦值給Expr1003,Expr1004,Expr1005,Expr1006
Expr1003=類型轉換(@1)
Expr1004=類型轉換(@2)
Expr1005=類型轉換(@3)
Expr1006=類型轉換(@4)
為什麽SQLSERVER不直接使用下面的執行計畫呢?
1 INSERT INTO [dbo].[SystemPara] ([RecordNo], [ParaValue], [Name], [Description] )2 VALUES(1,2,3,4)
還要類型轉換,參數代入這麽麻煩,SQLSERVER不是有病嗎???
這裡涉及到執行計畫重用,如果使用上面的執行計畫,編譯的時間是很快,但是
如果我插入的值是:9,8,6,7
1 INSERT INTO [dbo].[SystemPara] ([RecordNo], [ParaValue], [Name], [Description] )2 VALUES(9,8,6,7)
SQLSERVER不能重用上次的執行計畫,又要重建執行計畫,您說這樣的效率。。。。。。。。
最佳化(Optimization)
剛才說到選擇一種資料訪問路徑(執行計畫),現在繼續說一個請求(request)的生命週期的下一步:最佳化
在SQLSERVER裡面,最佳化意味著從多個選擇條件中選擇最佳的資料訪問路徑。
考慮一下,如果你有一個簡單的涉及到兩個表的join查詢,每個表都有額外的索引,
這裡就有4種可選的執行方案,去訪問表中的資料
因為有這麽多的可選方案,查詢複雜度已經比較高了,如果這時候表中的索引繼續增多的話,查詢複雜度有可能以指數的方式增長
再加上JOIN聯結本來就有三種聯結方式:nested loops join、merge join、hash join
可想而知,最佳化這個名詞在SQLSERVER裡是多麼重要,SQLSERVER使用一個查詢最佳化工具來預估這中間要消耗的時間,IO,CPU
查詢最佳化工具會考慮各種執行方案,SQLSERVER會儘力基於每種執行方案的開銷去作出評估,然後儘可能選擇一個開銷最低的
執行方案。SQLSERVER首先會計算在現有的表資料量下各種執行方案各自需要多少的開銷。為了選出一個開銷最低的執行方案,
SQLSERVER需要知道做聯結的每張表的資料量和表裡面各個欄位的資料的分布,這就需要靠統計資訊,
因為統計資訊本來就是用來統計這些資料的。另外一個要考慮的因素就是,每種執行方案所需要的CPU消耗和記憶體消耗
綜合以上各種因素,SQLSRVER會在每種執行方案裡算出一個cost值
SQLSERVER會在這些執行方案裡選出一個cost值最低的執行方案作為執行計畫執行
大家看一下,SQLSERVER要對上面各種因素進行考慮,這裡考慮是需要時間的,所以為什麽SQLSERVER
需要將執行計畫緩衝到記憶體裡以便將來繼續使用這個執行計畫,就是為了節省編譯時間
將來同樣的請求進入到SQLSERVER,並且這些請求能夠在CACHE裡找到一個已經編譯了和最佳化了的執行計畫
他們就能跳過查詢最佳化工具的最佳化階段
這裡一定要注意:同樣的請求進來SQLSERVER的時候,無論CACHE裡有沒有可以重用的執行計畫,SQLSERVER都需要
對請求裡的SQL語句進行解析,所以我上面才說:就是為了節省編譯時間 而不是 就是為了節省解析/編譯時間
解釋和編譯模組(模組化)
執行(Execution)
一旦查詢最佳化工具選擇了一個執行計畫,請求(request)就可以開始執行了。執行計畫會被翻譯成為一棵實際的執行樹
每個樹節點都是一個操作符,所有操作符都會實現一個有3個方法的抽象介面,分別是open(), next(), close()
如果閣下是C#程式員或者是JAVA程式員,一定不難理解什麼是介面,什麼是方法,什麼是抽象介面
MSDN裡有相關的資料:Showplan 邏輯運算子和物理運算子參考
查詢計劃是由物理運算子組成的一個樹(執行樹)
邏輯運算子
邏輯運算子描述了用於處理語句的關係代數操作。 換言之,邏輯運算子從概念上描述了需要執行哪些操作。
物理運算子
物理運算子實施由邏輯運算子描述的操作。 每個物理運算子都是一個執行某項操作的對象或常式。 例如,某些物理運算子可訪問表、索引或視圖中的列或行。 其他物理運算子執行其他動作,如計算、彙總、資料完整性檢查或聯結。 物理運算子具有與其關聯的開銷。
物理運算子初始化、收集資料,然後關閉。 具體來講,物理運算子可以響應下列三種方法調用:
Init():Init() 方法使物理運算子初始化自身並設定所有需要的資料結構。 儘管一個物理運算子通常只接收一次 Init() 調用,但也可以接收許多次調用。
GetNext():GetNext() 方法使物理運算子獲得資料的第一行或後續行。 物理運算子可以不接收 GetNext() 調用,也可以接收許多次調用。
Close():Close() 方法使物理運算子執行某些清除操作,然後關閉。 一個物理運算子只接收一個 Close() 調用。
GetNext() 方法返回一個資料行,它的調用次數作為 ActualRows 顯示在使用 SET STATISTICS PROFILE ON 或 SET STATISTICS XML ON 產生的執行程序表輸出中。 有關這些 SET 選項的詳細資料,請參閱 SET STATISTICS PROFILE (Transact-SQL) 和 SET STATISTICS XML (Transact-SQL)。
文中說的操作符實際上指的就是物理運算子:三個方法指的是open()=init(),next()=getnext(),close()=close()
每個物理運算子就是調用自己的三個方法
在SQLSERVER執行請求的過程中,執行樹的根節點會不斷迴圈的調用open(),然後重複調用next()直到返回false值
最後調用close()。樹的根節點的運算子會依次調用他的子節點的同樣的運算子,而子節點又會依次調用他的子節點的同樣的運算子
一直調用下去。在樹的葉子節點一般都會是讀取表資料或表索引的物理運算子。而執行樹的中間節點一般都是一些實現不同資料操作的運算子
例如:過濾表資料、join串連、對資料排序。那些使用並行的查詢會使用一個特別的運算子叫做:Exchange Oprators(交換操作)
交換操作運算子在執行的過程中會使用多線程(tasks => workers),調用每個線程去執行子樹的執行計畫,
然後彙總這些運算子的輸出結果,在這個過程中會使用典型的(多生產者《-》一個消費者模式)。
關於Exchange Oprators(交換操作) 可以參考這篇文章:SQL Server 2000中的平行處理和執行計畫中的位元影像運算子
我們使用 SET STATISTICS PROFILE ON 就可以看到執行樹,下面是一些列的名稱,更詳細的就不說了,網上有很多資料
NodeId:樹節點
Parent:父節點
PhysicalOp:物理運算子
LogicalOp:邏輯運算子
這種執行樹的執行模型不單只應用於查詢,插入,刪除,更新的執行都是同樣利用執行樹來執行的
插入記錄、刪除記錄、更新記錄都會有相應的運算子
一個執行樹沒有子樹的情況
一個執行樹具有子樹的情況
如果執行樹具有子樹,他的執行方式也是從子樹的葉子節點開始執行,一直執行到樹的根節點
特別要介紹一下,這些運算子也有停止-繼續的行為特性,意思是說除非他們的子節點運算子已經吸收完所有的輸入,他們才能產生輸入
例如:排序運算子,排序運算子在最初調用next()函數的時候不會返回任何結果因為這時候他的子節點還沒有讀取完所有資料,
這時候需要停止執行next()函數(每個運算子建立出來就會不停調用next函數),直到他的子節點讀取完所有資料他才能對這些資料
進行排序(繼續調用next()函數),取出結果集並排序
如果資料已經緩衝在記憶體裡了,SQLSERVER就不需要去磁碟裡取資料,直接在記憶體裡取資料,記憶體裡的這塊空間,
SQLSERVER官方術語叫:Buffer pool
而在記憶體裡緩衝執行計畫的這塊空間,SQLSERVER官方術語叫:Plan Cache
執行模組(模組化)
結果(Results)
在執行完畢之後,SQLERVER會將結果集返回給用戶端應用程式
當執行到執行樹的根節點的時候,根節點通常負責將結果集寫入到網路緩衝區(network buffers)
然後將這些結果集發送回用戶端。一個完整的結果集還沒有建立完畢,一部分的結果首先會存放到中間儲存(記憶體或磁碟)
然後逐段逐段發送給用戶端,例如一個SQL語句查詢的結果需要返回10條記錄,有3條記錄已經產生好了,可以返回給用戶端了
SQLSERVER首先將這3條記錄放入中間儲存(記憶體或磁碟),也可以叫網路緩衝區,等用戶端來取走這3條記錄,如此類推。
返回結果集給用戶端的時候,SQLSERVER用的是網路流量控制協議。
如果用戶端沒有積極地將這些結果集取走(例如調用SqlDataReader.Read())。最終會導致網路流量控制組件不得不阻塞
結果集發送端並且會掛起查詢的執行。
只有網路流量控制組件協調和緩解了網路資源的需求(網路沒有阻塞),查詢才會恢複,並且繼續產生結果集
不知道大家有沒有遇到過等待類型:ASYNC_NETWORK_IO的等待
裡,用戶端二就要等待,在SQLSRVER裡查詢就會顯示ASYNC_NETWORK_IO類型的等待
有趣的是,OUTPUT參數的返回,OUTPUT參數的值會被插入到返回給用戶端的結果集的網路資料流中。
當請求完成的時候,OUTPUT參數值只能在查詢執行的最後寫到結果集中,這就是為什麽OUTPUT參數值
只有當所有的結果集都返回了才能檢查OUTPUT參數的值
查詢執行過程中要賦予的記憶體(Query Execution Memory Grant)
一些運算子需要固定的記憶體去執行他們的工作。排序運算子為了進行排序需要記憶體去儲存輸入到排序運算子的資料
Hash join和hash彙總必須建立大型的hash表去執行他們的工作。執行計畫知道那些未完成的運算子需要多少記憶體
根據運算子類型,預估的行記錄,運算子必須要處理統計資訊提供給他的表中的欄位的大小。
那些在執行計畫裡的運算子所需要的總的記憶體我們通常稱為記憶體賦予。
試想一下,當非常多的並發查詢被執行的時候,因為大量的昂貴的運算子(這些運算子一般都需要很多記憶體,所以稱之為昂貴的)
需要請求記憶體,在同一時間裡面他們能夠用盡電腦的記憶體。
為了阻止這種情況的發生,SQLSERVER使用一種叫“資源訊號量”的東西。這個東西能夠確保正在執行的查詢的總記憶體配置不會超過
當前電腦中的記憶體總和。當總的記憶體配置就快耗盡當前伺服器裡的可用記憶體的時候,正在執行的查詢必須要等待那些就快執行完畢
的查詢去釋放他們擁有的記憶體。
您可以查詢sys.dm_exec_query_memory_grants這個DMV視圖來擷取當前的記憶體配置(請求的記憶體,分配了的記憶體)
當一個查詢必須要等待記憶體的賦予/分配,在SQL PROFILER裡可以看到Execution Warnings 事件類型
Execution Warnings 事件類型指出了當SQL語句或者預存程序執行的過程中的記憶體配置警告
這個事件類型能夠監視必須要等待一秒或更多記憶體的某些查詢,或者擷取記憶體失敗的查詢
在SQL PROFILER裡,一些與記憶體有關的事件類型
Exchange Spill 事件類型
Sort Warnings 事件類型:排序的時候所需記憶體不足
Hash Warning 事件類型
相關語句
1 select * from sys.dm_exec_query_resource_semaphores2 3 4 select * from sys.dm_exec_query_memory_grants
1 SELECT [session_id], 2 [request_id], 3 [start_time], 4 [status], 5 [command], 6 [wait_type], 7 [text_size], 8 [language] , 9 [transaction_isolation_level],10 [row_count],11 [granted_query_memory],12 [executing_managed_code]13 FROM sys.[dm_exec_requests]
我如何利用這些資訊(How can I use all this information)
上面的資訊有可能幫您解決performance troubleshooting problems(效能問題)
一旦您明白了您的用戶端正在發送多個請求到SQLSERVER,SQLSERVER端正在建立多個任務(task)去處理
您發給他的請求,效能的謎題就可以很簡單地解決了:很多時候,您的任務不是正在執行(正在佔領CPU)就是處於正在等待
每次等待,SQLSERVER都會依靠內部等待統計資訊去收集等待的資訊(等待什麼和等了多久)。
利用收集回來的統計資訊去解決效能瓶頸是非常好的方法
附上兩張完整的圖
總結
文中好像遺漏了Scheduler
Scheduler
對於每個邏輯CPU,SQLSERVER會有一個scheduler與之對應,在SQL層面上代表CPU對象,
只有拿到scheduler所有權的worker才能在這個邏輯CPU上運行
翻譯完結了~
如果對閣下有協助的話,希望給個推薦吧o(∩_∩)o
相關文章:
SQL Server 效能問題—等待RESOURCE_SEMAPHORE
如何知道TSQL語句已經運行了多久
SQL Server 串連加密 (1) -- SQL Server connection encyption
如有不對的地方,歡迎大家拍磚o(∩_∩)o
-----------------------------------------------------------------------------------------
2013-10-26 補充
關於時間統計
1 SET STATISTICS TIME ON 2 GO
1 SQL Server 分析和編譯時間: 2 CPU 時間 = 0 毫秒,佔用時間 = 58 毫秒。3 4 SQL Server 執行時間:5 CPU 時間 = 0 毫秒,佔用時間 = 1 毫秒。
我覺得SQLSERVER顯示出來的資料統計資料應該就是在分析和編譯模組和執行模組的開頭和結尾插入時間統計代碼
來統計出所使用的時間的
關於時間統計範圍
圖中紅色圓圈部分我認為是SQLSERVER團隊插入時間統計代碼的地方
分析和編譯時間:無論有沒有plan cache,從進入命令分析器開始,到離開查詢最佳化工具結束
執行時間:從查詢執行器開始,都離開查詢執行器結束
有人會覺得應該是結果集存放在網路緩衝區或者結果集真正到用戶端的手裡才算是執行時間的結束
-------------------------------------------------------------------------
但是我不這麽認為,到達網路緩衝區之前結果集已經產生好了,表示查詢執行完畢了
查詢執行完畢的意思:所有結果都已經產生好了,不是說客戶要10條記錄先產生好3條記錄,將這3條記錄先放入網路緩衝區待用戶端取走
這樣統計是不科學的,應該是10條記錄都已經產生好了(為標準),並且在傳送到網路緩衝區之前
用戶端有沒有取走,結果集什麼時候到達用戶端,SQLSERVER並不需要關心,因為各種的情況,例如:網路阻塞
這個不能算在SQLSERVER的執行時間上
SQLSERVER團隊不可能將時間統計代碼寫在用戶端上吧,用戶端又不屬於SQLSERVER,SQLSERVER團隊怎麽將
時間統計代碼寫在用戶端的應用程式裡啊???
不知道您們的意見如何呢???
帶您理解SQLSERVER是如何執行一個查詢的