【原創】c#實現用SQL池(多線程),定時批量執行SQL語句
在實際項目開發中,業務邏輯層的處理速度往往很快,特別是在開發Socket通信服務的時候,網絡傳輸很快,但是一旦加上數據庫操作,性能一落千丈,數據庫操作的效率往往成為一個系統整體性能的瓶頸。面對這問題,我們怎么辦呢?好,下面我就為大家介紹一種方法:構建SQL池,分離業務邏輯層和數據訪問層,讓業務邏輯層從低效的數據庫操作解脫,以提高系統整體性能。
(一)SQL池
SQL池是SQL容器,用于存放業務邏輯層拋過來的SQL語句。SQL池主要提供以下幾種方法:
1)internal string Pop(),從池中取出SQL。
2)internal void Push(string item),增加一個SQL到池中。
3)internal string[] Clear(),清空SQL池,清空前,返回SQL池中所有SQL語句。
特別提醒一下,SQL池是面向多線程的,所以必須對公共資源SQL采取鎖機制。這里采用互斥鎖,當業務邏輯層線程往SQL池中拋入SQL語句時,禁止SQL執行線程執行SQL語句,反之,當SQL執行線程執行SQL語句時,也不允許業務邏輯層線程往SQL池中拋入SQL語句。為什么要這么做?因為SQL執行線程是批量執行SQL語句,在批量執行SQL語句前,會從池中取出所有SQL語句,如果此時業務邏輯層線程往SQL池中拋入SQL語句,則會導致這些SQL語句丟失,得不到執行。
下面是SQL池代碼:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; namespace Test1 { sealed class SQLPool { //互斥鎖 public static Mutex mutexSQLPool = new Mutex(); //SQL池 Stack<string> pool; /// <summary> /// 初始化SQL池 /// </summary> internal SQLPool() { this.pool = new Stack<string>(); } /// <summary> /// 獲取SQL池數量 /// </summary> internal Int32 Count { get { return this.pool.Count; } } /// <summary> /// 從池中取出SQL /// </summary> /// <returns></returns> internal string Pop() { lock (this.pool) { return this.pool.Pop(); } } /// <summary> /// 增加一個SQL到池中 /// </summary> /// <param name="item"></param> internal void Push(string item) { if (item.Trim() == "") { throw new ArgumentNullException("Items added to a SQLPool cannot be null"); } //此處向SQL池中push SQL必須與Clear互斥 mutexSQLPool.WaitOne(); try { this.pool.Push(item); //此處如果出錯,則不會執行ReleaseMutex,將會死鎖 } catch { } mutexSQLPool.ReleaseMutex(); } /// <summary> /// 清空SQL池 /// 清空前,返回SQL池中所有SQL語句, /// </summary> internal string[] Clear() { string[] array = new string[] { }; //此處必須與Push互斥 mutexSQLPool.WaitOne(); try { array = this.pool.ToArray(); //此處如果出錯,則不會執行ReleaseMutex,將會死鎖 this.pool.Clear(); } catch { } mutexSQLPool.ReleaseMutex(); return array; } } }
(二)SQL池管理
SQL池管理主要用于管理SQL池,向業務邏輯層線程和SQL執行線程提供接口。
業務邏輯層線程調用 public void PushSQL(string strSQL) 方法,用于向SQL池拋入SQL語句。
SQL執行線程調用 public void ExecuteSQL(object obj) 方法,用于批量執行SQL池中的SQL語句。
注意,SQL池管理類采用單例模型,為什么要采用單例模型?因為SQL池只能存在一個實例,無論是業務邏輯層線程還是SQL執行線程,僅會操作這一個實例,否則,將會導致SQL池不唯一,SQL執行無效。
下面是SQL池管理類代碼:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Test1 { class SQLPoolManage { //單例模型 public static readonly SQLPoolManage sqlPoolManage = new SQLPoolManage(); #region 屬性 SQLPool poolOfSQL; #endregion #region 構造函數 /// <summary> /// 初始化 /// </summary> public SQLPoolManage() { this.poolOfSQL = new SQLPool(); } #endregion #region 方法 /// <summary> /// 將SQL語句加入SQL池中 /// </summary> /// <param name="strSQL"></param> public void PushSQL(string strSQL) { this.poolOfSQL.Push(strSQL); } /// <summary> /// 每隔一段時間,觸發ExecuteSQL /// ExecuteSQL用于執行SQL池中的SQL語句 /// </summary> /// <param name="obj"></param> public void ExecuteSQL(object obj) { if (this.poolOfSQL.Count > 0) { string[] array = this.poolOfSQL.Clear(); //遍歷array,執行SQL for (int i = 0; i < array.Length; i++) { if (array[i].ToString().Trim() != "") { try { //數據庫操作 //...... } catch { } } } } } #endregion } }
(三)定時觸發SQL執行線程
總結有以下三種方法,具體請參見http://www.cnblogs.com/tianzhiliang/archive/2010/08/31/1813928.html:
方法一:調用線程執行方法,在方法中實現死循環,每個循環Sleep設定時間;
方法二:使用System.Timers.Timer類;
方法三:使用System.Threading.Timer;
代碼如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; namespace Test1 { class Program { static void Main(string[] args) { //向SQL池中拋入SQL語句 SQLPoolManage.sqlPoolManage.PushSQL("delete from tbl_test where id = 1"); //定時觸發SQL執行線程 System.Threading.Timer threadTimer = new System.Threading.Timer(new System.Threading.TimerCallback(SQLPoolManage.sqlPoolManage.ExecuteSQL), null, 0, 100); Console.ReadLine(); } } }