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的策略檔案:
<?xml version="1.0" encoding ="utf-8"?>
<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來監聽策略請求和發送
this._policyThread = this.CreateThread(this.PolicyRequest);
//建立Socket來監聽資訊請求和發送
this._infoThread = this.CreateThread(this.InfoRequest);
//建立Socket來監聽資料庫跟蹤請求和發送
this._traceServerThread = this.CreateThread(this.TraceServerRequest);
PolicyRequest,InfoRequest的方法具體看文章末尾的附件原始碼,這裡主要說一下TraceServerRequest的方法:
SqlConnectionInfo conninfo = new SqlConnectionInfo();
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用戶端:
while (true)
{
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>
/// 當接收完成
/// </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