技术开发 频道

使用TMO监测和诊断SQL Server

  【IT168 技术文档】大家一定用过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里很多组件是基于它建立,所以必须增加这个引用

  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"]);
}
0
相关文章