Sql Server Profiler作为Microsoft Sql Server数据库系列的性能工具,通过它可以对数据库的运行状况进行实时跟踪,从中可以找到慢查询或者死锁的SQL语句,从而去优化系统。本文介绍如果通过Silverlight来对Sql Server Profiler进行实时监控(数据库环境以Sql Server 2005为例)
首先,先来看下SqlServer数据库自带的性能工具Sql Server Profiler,新建一个跟踪:
我们选择默认的模板Standard,其他都不变,点击运行:
从中可以看到数据库中的SQL的运行状态,EventClass作为事件类型,TextData作为运行的SQL脚本,ApplicationName作为一个应用程序的执行源的名称,比如说,从.Net应用程序执行的SQL脚本就是.Net SqlClient Data Provider,再比如说,我直接从数据库客户端工具执行一条SQL语句,可以看到ApplicationName为 ”Microsoft SQL Server Management Studio - 查询“,因此我们通过ApplicationName字段得到程序的执行源,LoginName作为一个数据库的账户,一般系统用户为sa,包括CPU/Reads/Writes在此上面也是一目了然,Duration比较有用,可以查看执行语句的耗时,后面还包括StartTime的开始时间和EndTime的结束时间,当然一些隐藏的列我就不一一列举了,有兴趣大家可以勾选查看下。
如果我要定义一个符合自己需要的模板呢,那么可以通过新建/编辑模板来实现:
上面有很多的事件类型的选择,可以根据自己的需要定制Event,这里我以最简单的方式,就选择了TSQL:SQL BatchCompleted:
并且列选择一个数据库DatabaseName,类似于编辑 MyEnt,这样该模板文件只会在MyEnt为名称的数据库中进行SQL执行跟踪了:
最后点击保存即可
那么,在.Net上怎么实现对于Sql Server Profiler的使用和监控,实际上,Sql ServerProfiler本身为.Net开发的,后来我在SqlServer2005数据库的安装目录 C:\Program Files\Microsoft SQL Server\90\SDK\Assemblies 中,找到一个Microsoft.SqlServer.ConnectionInfo.dll,这个dll就可以实现执行Sql的监控。
开始创建项目,其中,监控程序是一个控制台程序,而Silverlight客户端程序是一个对于SqlServerProfiler实时监控的展示,Silverlight的客户端程序通过Socket和监控程序进行通信。由于Silverlight对于TCP通信的限制,必须往监控程序发送策略请求,并且对于TCP通信来说,目前Silverlight的使用端口必须为4502-4530,于是必须在监控端先设置一个Policy.xml的策略文件:
< access-policy >
< cross-domain-access >
< policy >
< allow-from >
< domain uri ="*" />
</ allow-from >
< grant-to >
< socket-resource port ="4502-4530" protocol ="tcp" />
</ grant-to >
</ policy >
</ cross-domain-access >
</ access-policy >
现在我将策略请求,监听SL客户端连接/数据收发请求,数据库监听跟踪请求,分别创建3个线程:
//创建Socket来监听策略请求和发送
// 创建Socket来监听信息请求和发送
this._infoThread = this.CreateThread( this.InfoRequest);
// 创建Socket来监听数据库跟踪请求和发送
this._traceServerThread = this.CreateThread( this.TraceServerRequest);
PolicyRequest,InfoRequest的方法具体看文章末尾的附件源代码,这里主要说一下TraceServerRequest的方法:
conninfo.ServerName = " 机器名 ";
conninfo.UserName = " 数据库账户 ";
conninfo.Password = " 数据库密码 ";
conninfo.UseIntegratedSecurity = false;
TraceServer trace = new TraceServer();
trace.InitializeAsReader(conninfo, TDF_FILE);
SqlConnectionInfo, TraceServer类来自于对Microsoft.SqlServer.ConnectionInfo的引用,其中TDF_FILE就是我前面说的跟踪模板文件名,它的文件后缀为tdf。
while (trace.Read())
{
var eventData = new EventData()
{
EventClass = trace[ " EventClass "].ToString(),
TextData = trace[ " TextData "] != null ? trace[ " TextData "].ToString() : "",
ApplicationName = trace[ " ApplicationName "] != null ? trace[ " ApplicationName "].ToString() : "",
NTUserName = trace[ " NTUserName "] != null ? trace[ " NTUserName "].ToString() : "",
LoginName = trace[ " LoginName "] != null ? trace[ " LoginName "].ToString() : "",
CPU = trace[ " CPU "] != null ? trace[ " CPU "].ToString() : "",
Reads = trace[ " Reads "] != null ? trace[ " Reads "].ToString() : "",
Writes = trace[ " Writes "] != null ? trace[ " Writes "].ToString() : "",
Duration = trace[ " Duration "] != null ? trace[ " Duration "].ToString() : "",
ClientProcessID = trace[ " ClientProcessID "] != null ? trace[ " ClientProcessID "].ToString() : "",
SPID = trace[ " SPID "] != null ? trace[ " SPID "].ToString() : "",
StartTime = trace[ " StartTime "] != null ? trace[ " StartTime "].ToString() : "",
EndTime = trace[ " EndTime "] != null ? trace[ " EndTime "].ToString() : "",
};
var sendData = JsonConvert.SerializeObject(eventData);
if ( string.IsNullOrEmpty(sendData))
continue;
lock (_lock)
{
this._queues.Enqueue(sendData);
}
}
通过trace.Read()方法来得到trace的跟踪记录,这里我不会马上通过Socket发送到SL客户端,而是通过一个队列对跟踪记录进行人队,通过InfoRequest从队列中取出数据,发送给SL客户端:
{
lock (_lock)
{
if ( this._queues.Count == 0)
{
Thread.Sleep( 200);
continue;
}
var sendData = this._queues.Dequeue();
Console.WriteLine(sendData);
byte[] data = Encoding.GetEncoding( " gb2312 ").GetBytes(sendData);
// 移除已断开的套接字
this._clientList.RemoveAll(o => o.Connected == false);
foreach (Socket s in this._clientList)
{
if (s.Connected)
{
try
{
// 发送数据
s.Send(data);
}
…
SL客户端通过Socket异步编程接收数据
/// 当接收完成
/// </summary>
/// <param name="e"></param>
void ProcessReceive(SocketAsyncEventArgs e)
{
if (e.SocketError == SocketError.Success)
{
try
{
Gb2312Encoding encoding = new Gb2312Encoding();
string data = Regex.Replace(encoding.GetString(e.Buffer, 0, e.Buffer.Length), @" \0 ", "");
EventData eventData = JsonConvert.DeserializeObject<EventData>(data);
byte[] bytes = new byte[ 1024];
e.SetBuffer(bytes, 0, bytes.Length);
this._syn.Send( this.GetText, eventData);
// 执行连接
this._socket.ReceiveAsync( this._socketArgs);
}
…
// 同步上下文调用的方法
private void GetText( object data)
{
if (data == null)
return;
EventData eventData = (EventData)data;
_list.Add(eventData);
this.Dispatcher.BeginInvoke( new Action(() =>
{
this.dataGridRecords.ItemsSource = null;
this.dataGridRecords.ItemsSource = _list;
this.dataGridRecords.UpdateLayout();
this.dataGridRecords.SelectedIndex = _list.Count - 1;
this.dataGridRecords.ScrollIntoView( this.dataGridRecords.SelectedItem, null);
}));
}
这样就实现了Silverlight客户端对于Sql Server Profiler的实时监控。
两个项目很简单:
先运行 SqlServerProfier.Host程序:
正在对外发送监控数据,
然后运行宿主SL客户端程序的Web端:
如果是慢查询的部分,我会用红色把它标红,从而确定慢查询语句,优化系统
总结:
当然,你还可以在监控程序中,对于跟踪记录进行Log写入或者一个特定Log表的插入,通过Web端(不管是Silverlight还是asp.net mvc均可以查询相关的跟踪记录),从而达到对于数据库SQL执行的监控。
附上源代码:SqlServerProfiler.rar