帶您理解SQLSERVER是如何執行一個查詢的

原文地址:http://www.cnblogs.com/lyhabc/p/3367274.htmlhtml

看這篇文章以前,閣下能夠先看一下下面的文章程序員

SQLSERVER獨特的任務調度算法"SQLOS"算法

SQL Server SQLOS 的任務調度[轉]sql

 

翻譯自:數據庫

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以來的知識去翻譯這篇文章session

但願你們多多支持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 e
12 ON e.[VC_IC_CardNO]=d.[VC_FD_Cardno]
13 INNER JOIN
14 [dbo].[CT_OuterCard] AS f
15 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子句裏

例如:cola=@parameter OR @parameter IS NULL

對於開發者必定要避免這種狀況。

這個時候,編譯必定要得出一種通用的執行計劃,不管任何參數代入到這個執行計劃裏都能得出最優的結果

在TSQL裏的參數化(Dynamic Search Conditions)

例以下面SQL語句:

1 SET STATISTICS PROFILE ON
2 GO
3 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_semaphores
2 
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團隊怎麼將

時間統計代碼寫在客戶端的應用程序裏啊???

 

不知道您們的意見如何呢???

相關文章
相關標籤/搜索