PDA

View Full Version : مانیتورینگ database



محمد رضا فاتحی
سه شنبه 16 تیر 1394, 17:51 عصر
سلام دوستای خوبم
یک سیستم برخط باید طراحی بشه که به صورت لحظه ای هر تغییری که توی دیتابیس رخ می ده رو به کاربر نشون بده و شامل چهار حالته
یکی از روش های ساده استفاده از نخ و while(true) که همه می دونن کار خیلی هزینه بری... خودم امتحان کردم usage cpu تو سیستم من cpu i7 حدود 85 درصد شد!!!!

یکی دیگه از روشها استفاده از کلاس SqlDependency بعد از کلی اینور اونور زدن به نمونه کد زیر رسیدم



private string cnnstring = "connectionSTR";
private void dep_onchanged(object sender, SqlNotificationEventArgs e)
{
SqlDependency dep = (SqlDependency)sender;
dep.OnChange -= dep_onchanged;
}
DataTable selectdt()
{
SqlDependency.Stop(cnnstring);
SqlDependency.Start(cnnstring);
string command = "select name from student where id='0'";
SqlConnection con = new SqlConnection(cnnstring);
SqlCommand com = new SqlCommand(command, con);
com.Notification = null;
DataTable dt = new DataTable();
SqlDataAdapter da = new SqlDataAdapter();
da.SelectCommand = com;
SqlDependency dep = new SqlDependency(com);
dep.OnChange += dep_onchanged;
con.Open();
da.Fill(dt);
con.Close();
return dt;
}
private void buttonX1_Click(object sender, EventArgs e)
{
selectdt();
}


البته این کار الزاماتی داره که باید سمت SQl انجام بشه اینم کوئری مورد نظر


alter database mydb set enable_Broker
create queue changed_queue
create service changed_srv on queue changed_queue('http://schemas.microsoft.com/sql/notifications/postquerynotification');
grant SUBSCRIBE query notifications to myLoginuser



خوب اشتباه نکنید.... آموزش نیست!!!!!!

سوالای من:
تو بحث sql به دومورد بر خوردم
الف) دستور alter database mydb set enable_Broker خیلی طول کشید و اصلا اجرا نشد به جاش اینو بکار بردم

alter database mydb set enable_broker with rollback immediate;


ب) تو این دستور هم

create service changed_srv on queue changed_queue('http://schemas.microsoft.com/sql/notifications/postquerynotification');

خطای دسترسی به آدرس رو میداد که من حذفش کردم

create service changed_srv on queue changed_queue;


تا اینجا آیا این دوتا کار خللی به روند اجرای کلاس SqlDependency وارد نمی کنه؟؟

ثانیا بر فرض خللی ایجاد نشد... چکار کنیم که آنلاین تشخیص بده... تو یه نمونه کد استخراج اطلاعات رو گذاشته بود تو یه دکمه در صورتی که تو برنامه من قراره سرور دیتابیس رو تغییر بده کلاینت ها متوجه بش

ثالثا آیا روش یا متد کلاس یا هرچیز دیگه ای که بهتر عمل کنه می شناسید؟؟

ببخشید طولانی شد!!

خیلی درگیر این مساله هستم... برنامه تموم شده و تو مقیاس آزمایشگاهی تست شده... ولی این مساله(استفاده از حلقه بی نهایت و نخ) هزینه بره برای سیستم های مقصد

jeson_park
چهارشنبه 17 تیر 1394, 17:38 عصر
نیازی به create service نیست و خود دستور alter database mydb set enable_Broker سرویسش رو فعال می کنه فقط بدونید کجا توابع start و stop مربوط به SqlDependency صدا میزنید
اگر برنامه تحت شبکه نیست و دیتا بیس لوکال هست اون create queue رو هم بردارید باز مشکل نداره

محمد رضا فاتحی
چهارشنبه 17 تیر 1394, 17:57 عصر
ممنون جواب دادید... ولی کار نکرد دیتابیس هم تحت شبکس
یه نمونه کد چیزی دارید؟؟

محمد آشتیانی
پنج شنبه 18 تیر 1394, 15:50 عصر
سلام جناب فاتحی
عرض شود به حضور شما که ، برای فعال کردن ServiceBroker باید ابتدا دیتا بیس رو در حالت Single User قرار بدید تا کوئری بدون مشکل اجرا بشه (اسم دیتابیس فرضی SampleDB هست و جدول فرضی myTable)

ALTER DATABASE SampleDB SET SINGLE_USER;
ALTER DATABASE SampleDB SET ENABLE_BROKER;
ALTER DATABASE SampleDB SET MULTI_USER;


ساخت Service و Queue هم با کوئری های زیر بدون مشکل انجام میشه ، البته لازم به ذکره که بدون ساختن این موارد هم ظاهرا کلاس Sql Dependency خودش این موارد رو مدیریت میکنه

CREATE QUEUE myTableMessages;


CREATE SERVICE myTableNotifications
ON QUEUE myTableMessages
([http://schemas.microsoft.com/SQL/Notifications/PostQueryNotification]);


تا اینجا تنظیمات سمت SqlServer تمومه ، حالا میریم سراغ کد نویسی کلاینت
این کلاس رو برای ساخت آبجکت SqlDependency و تولید رویدادهای مورد نیاز نوشتم.

using System;
using System.Data;
using System.Data.SqlClient;


namespace NotifyService
{
public class SqlNotifier : IDisposable
{
public SqlCommand SqlCmd { get; set; }
public SqlConnection SqlCon { get; set; }


public void StartMonitoring()
{
SqlDependency.Start(this.SqlCon.ConnectionString);
RegisterDependency();
}


private event EventHandler<SqlNotificationEventArgs> _NewNotification;
public event EventHandler<SqlNotificationEventArgs> NewNotification
{
add { this._NewNotification += value; }
remove { this._NewNotification -= value; }
}


public virtual void OnNewNotification(SqlNotificationEventArgs notification)
{
if (this._NewNotification != null)
this._NewNotification(this, notification);
}


public void RegisterDependency()
{
this.SqlCmd = new SqlCommand(SqlCmd.CommandText,this.SqlCon);
this.SqlCmd.Notification = null;


SqlDependency dependency = new SqlDependency(this.SqlCmd);
dependency.OnChange += this.Dependency_OnChange;


if (this.SqlCon.State == ConnectionState.Closed)
this.SqlCon.Open();


this.SqlCmd.ExecuteReader();
this.SqlCon.Close();
}


private void Dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
SqlDependency dependency = (SqlDependency)sender;
dependency.OnChange -= new OnChangeEventHandler(Dependency_OnChange);
this.OnNewNotification(e);
RegisterDependency();
}


public void Dispose()
{
SqlDependency.Stop(this.SqlCon.ConnectionString);
}


}
}




و اما نحوه استفاده از این کلاس
طبیعتا باید در هر فرمی که میخواید این قابلیت رو داشته باشید باید فضای نام NotifyService رو که مربوط به کلاس بالا هست اضافه کنید.
using NotifyService;

از کلاس بالا ، با دسترسی در سطح فرم یک نمونه بسازید.
private SqlNotifier NS = new SqlNotifier();

در متد سازنده فرمتون این کدها رو بنویسید
- خط اول که کانکشن استرینگ رو به کلاس پاس میدید.
- خط دوم هم مشخصه (شما تغییرات مربوط به جدولی که در این کوئری مشخص شده رو دریافت خواهید کرد - نکته مهم اینه که حتما و حتما اسم جدول باید به صورت دو بخشی نوشته بشه یعنی dbo.myTable )
- خط بعد سرویس رو اجرا میکنه
- نهایتا در خط چهارم event handler به نام NewNotification رو در کلاس فوق الذکر به متدی به نام DependencyEventHandler که در فرم می نویسیم و و به اون اشاره خواهم کرد رو به هم متصل میکنه.

NS.SqlCon = new SqlConnection(@"Data Source=127.0.0.1;Initial Catalog=SampleDB;Persist Security Info=True;User ID=sa;Password=***");
NS.SqlCmd = new SqlCommand("SELECT id, name FROM dbo.mytable");
NS.StartMonitoring();
NS.NewNotification += new EventHandler<SqlNotificationEventArgs>(DependencyEventHandler);


در واقع در متد DependencyEventHandler ، هر اتفاقی که مد نظرتون هست که در هنگام تغییرات در دیتابیس باید ایجاد بشه رو مینویسید (اینجا من در این متد ، متد LoadData رو فراخوانی کردم که با هربار تغییرات جدول مورد نظر ، محتویات دیتاگرید رو آپدیت میکنه.)

شکل متد DependencyEventHandler

private void DependencyEventHandler(object s, SqlNotificationEventArgs e)
{
LoadData();
}


متد LoadData که با هربار تغییرات جدول مورد نظر محتویات دیتاگرید رو آپدیت میکنه (کلاس DataAccess ، کلاسی هست که خودم نوشتم برای ارتباط با دیتابیس ، شما میتونید از همون آبجکت های ADO.Net مستقیم استفاده کنید.)

private delegate void _LoadData();
private void LoadData()
{
if (this.InvokeRequired)
{
this.Invoke(new _LoadData(LoadData));
return;
}
var da = new DataAccess();
var dt = new DataTable();


da.conOpen();
dataGridView1.DataSource = da.execReader("SELECT * FROM myTable");
da.conClose();
}


و در آخر باید در رویداد FormClosed ، نمونه ای رو که از کلاس بالا ساختید Dispose کنید.

private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
NS.Dispose();
}


* کانکشن استرینگ و کوئری ها رو بنا بر سیستم خودتون اصلاح بفرمائید.


فکر نمیکنم نکته مبهمی باقی مونده باشه دوست عزیزم.
موفق باشید.

محمد رضا فاتحی
جمعه 19 تیر 1394, 16:40 عصر
واقعا ممنونم خیلی لطف کردین
*************************************************
امتحان کردم متاسفانه کار نکرد.....
می تونه از سیستم یا نوع sql باشه؟؟ من sql2008R2 دارم

محمد آشتیانی
جمعه 19 تیر 1394, 18:04 عصر
سلام
عرض به حضور شما ، کدی که خدمت شما دادم به خوبی کار میکنه ، اتفاقا بنده هم از همون نسخه اسکیوال سرور استفاده میکنم
شما با چه لاگینی به sql server وصل میشی؟ شاید دسترسی لازم رو به دیتابیس مورد نظرت نداره اون لاگین

ali_72
شنبه 20 تیر 1394, 06:54 صبح
من تو وب برای این کار از signalR استفاده می کنم
میشه از سیگنال آر تو ویندوز هم استفاده کرد
چرا از سیگنال آر استفاده نمی کنید؟

محمد رضا فاتحی
شنبه 20 تیر 1394, 08:00 صبح
سلام
عرض به حضور شما ، کدی که خدمت شما دادم به خوبی کار میکنه ، اتفاقا بنده هم از همون نسخه اسکیوال سرور استفاده میکنم
شما با چه لاگینی به sql server وصل میشی؟ شاید دسترسی لازم رو به دیتابیس مورد نظرت نداره اون لاگین

ولی متاسفانه برای من اصلا هیچ عکس العملی نشون نمی ده.... من برای دیتابیسم یه پوزر ایجاد کردم با دسترسی admin
***************************
آقا خییییییییییییییییییییلی ممنون.... روی سیستم دیگه امتحان کردم درست بود....

ghasem110deh
چهارشنبه 15 مهر 1394, 18:45 عصر
سلام
آقا یکم بیشتر در مورد این موضوع توضیح میدین ؟!
من الان تو فرم اصلی یه نمودار (چارت) دارم واسه خرید و فروش ماهانه ... میشه با این روش بعد از مثلا درج فاکتور جدید
نمودار تو فرم اصلی هم بروز بشه (چون نمودار داده ها رو توی لود فرم میخونه دیگه)

محمد آشتیانی
چهارشنبه 15 مهر 1394, 20:10 عصر
سلام
توضیحات کامل برای استفاده ازش داده شده ، بله شما میتونید روی جدولی که فاکتور داخلش ثبت میشه این سرویس رو فعال کنید و در ایونتی که با تغییرات در اون جدول اجرا میشه ، چارت رو آپدیت کنید.


موفق باشید.

ghasem110deh
پنج شنبه 23 مهر 1394, 13:33 عصر
سلام
این کد رو با کمک آقا رضا نوشتم ولی چیزی نشون نمیده !!!


private SqlNotifier NS = new SqlNotifier();
SqlConnection con = new SqlConnection(@"Data Source=.;Initial Catalog=Advanced_Search Test;Integrated Security=True");
public Form1()
{
InitializeComponent();
NS.SqlCon = con;
NS.SqlCmd = new SqlCommand("SELECT Id, Warehouse FROM dbo.Tbl_Warehouse");
NS.StartMonitoring();
NS.NewNotification += new EventHandler<SqlNotificationEventArgs>(DependencyEventHandler);
}


private void DependencyEventHandler(object sender, SqlNotificationEventArgs e)
{
loadData();
}


private void Form1_Load(object sender, EventArgs e)
{
CheckForIllegalCrossThreadCalls = false;
}


void loadData()
{
DataTable dt=new DataTable();
string command = "select Id, Warehouse FROM dbo.Tbl_Warehouse";
SqlDataAdapter da=new SqlDataAdapter(command,con);
try
{
con.Open();
da.Fill(dt);
con.Close();
dataGridView1.DataSource = dt;
}
catch (Exception)
{
con.Close();
}
}


private void button2_Click(object sender, EventArgs e)
{
string command = "insert into dbo.Tbl_Warehouse values('Anbar','0215555', 'tehran onvartar as dehat khodemon')";
SqlCommand com=new SqlCommand(command,con);
try
{
con.Open();
com.ExecuteNonQuery();
con.Close();
}
catch (Exception)
{
con.Close();
}
}


private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
NS.Dispose();
}
}


موارد توی درج به جدول اضافه میشه ولی چیزی نشون نمیده !
دیتابیس رو سینگل یوزر کردم ، برورکر هم فعال ... ولی دیگه مولتی یوزر نمیشه !

@liReza11800
شنبه 22 اسفند 1394, 04:07 صبح
با تشکر از آموزش خوبتون
ولی برای من خطای {"Invalid object name 'dbo.mytable'."} رو داد (سمت سی شارپ)

hamid_hr
شنبه 22 اسفند 1394, 07:36 صبح
خب
dbo.mytable رو بزار اسم جدول خودتون
مثلا
dbo.tbl1

@liReza11800
شنبه 22 اسفند 1394, 07:41 صبح
اسم جدولم همینه
مشکل از سرویس اس کیو ال بود درست شد
ممنون
ولی نمی ودنم چرا روی insert و delete فقط کار میکنه
ی رکورد رو آپدیت می کنم کار نمی کنه