使用Trace Management Object監測和診斷SQL Server

大家一定用過Profiler工具,我們可以用它來對SQL Server建立trace來監測某些感興趣的事件,也可以replay抓到的trace來診斷是哪些SQL語句的執行���成你的SQL Server耗費了大量的CPU資源。但Profiler是個GUI程序,有沒有辦法通過程序來抓trace和重放trace呢?也許有些讀者會想到用SQLCMD.exe執行sp_trace_create等存儲過程來操作,但那畢竟還是有些麻煩,這裏我們要介紹的Trace Management Object(TMO)則是.NET對象,你可以把它理解成trace/replay的API,你可以非常方便地在你的.NET程序中使用。請注意由於SQL Express版本不支持trace,因而TMO對象也無法在SQL Express版本上運行,即使是SQL Server 2008 Express with Advanced Services也不支持。

在SQL Server 2005裏TMO對象被實現在了Microsoft.SqlServer.ConnectionInfo.dll裏,在SQL Server 2008裏TMO對象則被移到了Microsoft.SqlServer.ConnectionInfoExtended.dll裏,但仍然在Microsoft.SqlServer.Management.Trace命名空間裏。下面我們將以SQL Server 2008爲例。

用VS2005新建一個Visual C#的Console Application工程,在Project菜單裏點擊Add Reference…增加對下表幾個組件的引用:

組件

描述

Microsoft.SqlServer.ConnectionInfo

需要SqlServerInfo類來建立對SQL的連接

Microsoft.SqlServer.ConnectionInfoExtended

TMO對象在這個Assembly裏

Micrososft.SqlServer.Management.Sdk.Sfc

SQL Server 2008裏很多組件是基於它建立,所以必須增加這個引用

組件描述Microsoft.SqlServer.ConnectionInfo 需要SqlServerInfo類來建立對SQL的連接 Microsoft.SqlServer.ConnectionInfoExtended TMO對象在這個Assembly裏 Micrososft.SqlServer.Management.Sdk.Sfc SQL Server 2008裏很多組件是基於它建立,所以必須增加這個引用

http://www.codeplex.com/上SQL Server 2008的Samples裏有個Readme_Tracer的例子(http://www.codeplex.com/MSFTEngProdSamples/Release/ProjectReleases.aspx?ReleaseId=18651),這個例子使用Standard.tdf模板啓動一個live trace,trace的內容將打印在Console窗口上,但把結果打印到Console窗口上非常亂,而且也沒有太大的實用價值,大家有興趣可以去參考一下。本文第一部分將介紹一個capture trace的示例,和Readme_Tracer有點類似,但我們會把trace結果輸出到trace文件,第二部分將介紹一個replay trace的示例,這也是Profiler最常用的兩個功能。

Capture trace示例

這個例子模仿你使用Profiler工具監測SQL Server操作的過程,程序啓動一個trace,將抓到的trace event輸出到文件中,等待60秒後退出。讀者可以嘗試將TraceFile類改爲TraceTable類來輸出到數據庫表中。下面是詳細的步驟和描述:

1、TraceServer類代表連接到SQL Server Instance的一個trace,下面的代碼演示瞭如何創建一個TraceServer對象。代碼前兩行使用Windows認證方式建立一個SqlConnectionInfo對象,你也可以通過提供用戶名/密碼的方式建立這個對象,InitializeAsReader函數的第二個參數是trace模板,這裏使用的TSQL_Replay模板,你需要根據你的SQL Server安裝目錄進行修改。

SqlConnectionInfo connInfo = new SqlConnectionInfo(".");
connInfo.UseIntegratedSecurity = true;
TraceServer traceServer = new TraceServer();
traceServer.InitializeAsReader(connInfo@"C:/Program Files/Microsoft SQL Server/100/Tools/Profiler/Templates/Microsoft SQL Server/100/TSQL_Replay.tdf"); 

2、TraceFile類代表一個trace文件,它既可以是capture trace的輸出文件,也可以是replay trace的輸入文件,下面的代碼將traceFile對象設爲traceServer所代表的trace的輸出文件,最後一行爲traceFile增加了一個WriteNotify事件的event handler,通過它我們將對輸出做一些過濾。

TraceFile traceFile = new TraceFile();
traceFile.InitializeAsWriter(traceServer@"d:/tracefile.trc");
traceFile.WriteNotify += new WriteNotifyEventHandler(WriteHandler); 

3、這兩個對象建立完畢,此時trace已經開始了,但tracefile.trc卻始終是0字節,爲什麼呢?因爲你還需要調用TraceFile類的Write函數來輸出,但Write函數有兩個問題,一是調用一次只輸出一個trace event,你需要不停地調用它;二是Write函數是同步的,如果沒有可以輸出的內容的話它會阻塞,所以你需要另起一個線程來調用Write函數。下面的代碼啓動WriteTraceProc線程並在60秒後結束capture trace。請注意traceServer.Close()必須在thread.Join()之前調用,否則WriteTraceProc線程可能會一直阻塞在Write函數調用上。

Thread thread = new Thread(WriteTraceProc);
thread.Start(traceFile); //pass traceFile as parameter
Thread.Sleep(60000);
lock (flagLock)
{
    exitFlag = true;
}
traceServer.Close();
thread.Join();
traceFile.Close(); 

4、接着是flagLock,exitFlag的定義及WriteTraceProc的代碼,WriteTraceProc將持續調用Write直到exitFlag被主線程置爲true。

private static object flagLock = new object();
private static bool exitFlag = false;
private static void WriteTraceProc(object obj)
{
    TraceFile traceFile = (TraceFile)obj;
    while (true)
    {
        lock (flagLock)
        {
            if (exitFlag)
                break;
        }
        traceFile.Write();
    }
}

5、最後是WriteHandler的代碼,我們將使用EventClass列來過濾所有Audit Login及Audit Logout事件,你可以根據需要設置你的過濾條件。由於Books Online沒有提供詳細的文檔,你也許需要使用IDataRecordChanger接口的GetName()來枚舉所有你能使用的列。

private static void WriteHandler(object sender, TraceEventArgs args)
{
    IDataRecordChanger recordChanger = args.CurrentRecord;
    string eventClass = (string)recordChanger["EventClass"];
    if (eventClass.StartsWith("Audit"))
        args.SkipRecord = true;
} 

 

在這篇文章中我們將介紹一個replay trace的示例,通過重放抓到的trace文件來診斷應用程序在SQL Server上運行是否有問題。

Replay trace示例

這個例子模仿你使用Profiler工具對抓到的trace文件進行重放,從而對SQL Server及你的應用程序進行診斷的過程。下面是詳細的步驟和描述。

1、TraceReplay類是對trace file或trace table進行重放的關鍵類,你需要設置TraceReplay的三個屬性,Connection屬性代表你將在哪個數據庫實例上進行重放,Source屬性代表重放哪個文件或數據表,OutputFile代表重放的結果將輸出到哪個文件,你也可以使用OutputTable屬性指定將重放結果輸出到數據表中。下面的代碼將使用capture trace例子裏抓到的trace file作爲重放的輸入並將結果存爲traceoutput.trc文件。

TraceReplay replay = new TraceReplay();
SqlConnectionInfo connInfo = new SqlConnectionInfo(".");
connInfo.UseIntegratedSecurity = true;
replay.Connection = connInfo;
TraceFile traceFile = new TraceFile();
traceFile.InitializeAsReader(@"c:/tracefile.trc");
replay.Source = traceFile;
TraceFile traceFileOutput = new TraceFile();
traceFileOutput.InitializeAsReplayOutputWriter(@"c:/replayoutput.trc");
replay.OutputFile = traceFileOutput;

2、TraceReplay類還有個比較重要的屬性Options,你可以通過設置Options屬性來控制如何重放。Options屬性是個TraceReplayOptions對象,Profiler工具中重放配置對話框上的很多配置項都可以通過Options屬性來設置。下面的代碼中設置了3個屬性,Mode設置重放模式爲連接層面上同步(另一個模式爲SequentialReplay,指在所有連接上完全同步),NumberOfReplayThreads設置重放的線程數爲2,KeepResults將控制在重放結果文件中是否出現SQL等的執行結果,比如你的trace裏有select 1語句,那設置KeepResults爲false將避免在結果文件中出現select 1返回的result set。

replay.Options.Mode = ReplayMode.ConnectionLevelSync;
replay.Options.NumberOfReplayThreads = 2;
replay.Options.KeepResults = false;

3、TraceReplay類提供了5個事件,你可以通過提供你自己的event handler來進一步控制重放操作,其中最有用的event是ReplayEvent,它將在每個event被重放之前調用,你可以控制是否跳過該event的重放。下面的代碼在設置了ReplayEvent的處理函數後啓動重放。請注意Start函數是同步操作,它將一直阻塞直到重放結束,所以如果你需要在中途停掉的話,你可以在event handler裏或者其它線程中調用Stop函數。 

replay.ReplayEvent += new ReplayEventHandler(ReplayHandler);
replay.Start();
traceFile.Close();

4、這裏我們的ReplayHandler處理函數將對SPID進行過濾,所有SPID不是54的事件都將被忽略。代碼中空的catch是爲了避免某些event不含有SPID列會造成讀取該列失敗。 

private static void ReplayHandler(Object sender, ReplayEventArgs args)
{
    IDataRecordChanger recordChanger = args.CurrentRecord;
    int spid = 0;
    try
    {
        spid = (int)recordChanger["SPID"];
    }
    catch { }
    if (spid != 54)
        args.SkipRecord = true;
}

5、Replay的結果文件可以用Profiler工具打開查看,當然你仍然可以通過TraceFile打開,只要簡單的調用InitializeAsReader然後循環調用Read即可,下面的代碼將打出TextData列的內容。

TraceFile traceFileOutput = new TraceFile();
traceFileOutput.InitializeAsReader(@"c:/replayoutput.trc");
while (traceFileOutput.Read())
{
    Console.WriteLine(traceFileOutput["TextData"]);
}

軟件開發工程師 徐進

TMODemo.zip
發佈了35 篇原創文章 · 獲贊 2 · 訪問量 22萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章