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