http://blog.csdn.net/ta8210/article/details/1582162
相關技術: 串連池 引用記數 多線程 C#.Net Java
目錄 引言 資料庫連接池(Connection Pool)的工作原理 串連池關鍵問題分析 並發問題 交易處理 串連池的分配與釋放 串連池的配置與維護 關鍵議題 引用記數 如何?交易處理 管理串連池 結合代碼說明 構造方法 啟動服務StartService 停止服務StopService 申請 GetConnectionFormPool 釋放DisposeConnection 如何更新屬性 如何確定串連是否失效 使用線程管理串連池 threadCreate threadCheck
其他
引言
一般的資料庫應用程式大致都遵循下面的步驟: 初始化程式 使用者在UI上輸入操作 由使用者操作產生資料庫操作 將資料庫操作遞交到資料庫伺服器 .... (重複2~4) 關閉應用程式
而本文則著重講解上面第4步驟.在著一步驟中我們經常是,開啟資料庫連接操作資料庫,最後關閉資料庫.
在伺服器端程式設計上與資料庫的操作顯得十分重要,因為你要處理的資料操作十分巨大.如果頻繁建立資料庫連接頻繁關閉資料庫連接則會引起效率低下甚至引發程式崩潰.
也許我們可以有另一種操作資料庫的形式,我們可以在程式運行時開啟一個資料庫連接,讓這個串連永久存在直到程式'死亡',那麼這樣做也有不安全隱患,我們知道一個對象存在時間越長或被使用次數越多則它表現的越不穩定,著不穩定因素是由於對象內部可能存在的潛在設計問題產生,對於資料庫連接對象道理也一樣.我們不能保證一個Connection對象裡面能一點問題不存在.所以我們也不敢長時間將它長時間佔用記憶體.
既然有這麼多的問題由此我們需要一個能幫我們維護資料庫連接的東西-它就是串連池,網上有很多的串連池例子,但是多數都是簡單的例子,或者介紹比較複雜的串連池原理,沒有一個比較完整介紹和實現串連池的例子.這裡就介紹你如何自己製作一個串連池.
對於共用資源,有一個很著名的設計模式:資源集區(Resource Pool)。該模式正是為瞭解決資源的頻繁分配﹑釋放所造成的問題。為解決我們的問題,可以採用資料庫連接池技術。資料庫連接池的基本思想就是為資料庫連接建立一個“緩衝池”。預先在緩衝池中放入一定數量的串連,當需要建立資料庫連接時,只需從“緩衝池”中取出一個,使用完畢之後再放回去。我們可以通過設定串連池最大串連數來防止系統無盡的與資料庫連接。更為重要的是我們可以通過串連池的管理機制監視資料庫的串連的數量﹑使用方式,為系統開發﹑測試及效能調整提供依據。串連池的基本工作原理見下圖。
資料庫連接池(Connection Pool)的工作原理
串連池關鍵問題分析
1、並發問題
為了使串連管理服務具有最大的通用性,必須考慮多線程環境,即並發問題。這個問題相對比較好解決,因為各個語言自身提供了對並發管理的支援像java,c#等等,使用synchronized(java)lock(C#)關鍵字即可確保線程是同步的。使用方法可以參考,相關文獻。
2、交易處理
我們知道,事務具有原子性,此時要求對資料庫的操作符合“ALL-ALL-NOTHING”原則,即對於一組SQL語句要麼全做,要麼全不做。
我們知道當2個線程公用一個串連Connection對象,而且各自都有自己的事務要處理時候,對於串連池是一個很頭疼的問題,因為即使Connection類提供了相應的事務支援,可是我們仍然不能確定那個資料庫操作是對應那個事務的,這是由於我們有2個線程都在進行事務操作而引起的。為此我們可以使用每一個事務獨佔一個串連來實現,雖然這種方法有點浪費串連池資源但是可以大大降低交易管理的複雜性。
3、串連池的分配與釋放
串連池的分配與釋放,對系統的效能有很大的影響。合理的分配與釋放,可以提高串連的複用度,從而降低建立新串連的開銷,同時還可以加快使用者的訪問速度。
對於串連的管理可使用一個List。即把已經建立的串連都放入List中去統一管理。每當使用者請求一個串連時,系統檢查這個List中有沒有可以分配的串連。如果有就把那個最合適的串連分配給他(如何能找到最合適的串連文章將在關鍵議題中指出);如果沒有就拋出一個異常給使用者,List中串連是否可以被分配由一個線程來專門管理捎後我會介紹這個線程的具體實現。
4、串連池的配置與維護
串連池中到底應該放置多少串連,才能使系統的效能最佳。系統可採取設定最小串連數(minConnection)和最大串連數(maxConnection)等參數來控制串連池中的串連。比方說,最小串連數是系統啟動時串連池所建立的串連數。如果建立過多,則系統啟動就慢,但建立後系統的響應速度會很快;如果建立過少,則系統啟動的很快,響應起來卻慢。這樣,可以在開發時,設定較小的最小串連數,開發起來會快,而在系統實際使用時設定較大的,因為這樣對訪問客戶來說速度會快些。最大串連數是串連池中允許串連的最大數目,具體設定多少,要看系統的訪問量,可通過軟體需求上得到。
如何確保串連池中的最小串連數呢。有動態和靜態兩種策略。動態即每隔一定時間就對串連池進行檢測,如果發現串連數量小於最小串連數,則補充相應數量的新串連,以保證串連池的正常運轉。靜態是發現空閑串連不夠時再去檢查。
關鍵議題 引用記數
在分配、釋放策略對於有效複用串連非常重要,我們採用的方法也是採用了一個很有名的設計模式:Reference Counting(引用記數)。該模式在複用資源方面用的非常廣泛,我們把該方法運用到對於串連的分配釋放上。每一個資料庫連接,保留一個引用記數,用來記錄該串連的使用者的個數。具體的實現上,我們對Connection類進行進一步封裝來實現引用記數。被封裝的Connection類我們提供2個方法來實現引用記數的操作,一個是Repeat(被分配出去)Remove(被釋放回來);然後利用RepeatNow屬性來確定當前被引用多少,具體是哪個使用者引用了該串連將在串連池中登記;最後提供IsRepeat屬性來確定該串連是否可以使用引用記數技術。一旦一個串連被分配出去,那麼就會對該串連的申請者進行登記,並且增加引用記數,當被釋放回來時候就刪除他已經登記的資訊,同時減少一次引用記數。
這樣做有一個很大的好處,使得我們可以高效的使用串連,因為一旦所有串連都被分配出去,我們就可以根據相應的策略從使用池中挑選出一個已經正在使用的串連用來複用,而不是隨意拿出一個串連去複用。策略可以根據需要去選擇,我們有4策略可是使用:
1.ConnLevel_ReadOnly 獨佔方式
使用閒置實際串連分配串連資源,並且在該資源釋放回之前,該資源在串連池中將不能將其引用分配給其他申請者。如果串連池中所有實際串連資源都已經分配出去,那麼即使串連池可以在分配引用資源在該模式下串連遲將不會分配串連資源,串連池會產生一個異常,標誌串連池資源耗盡。
例:假如一個實際串連可以被分配5次,那麼使用該模式申請串連的話您將損失4個可分配的串連,只將得到一個串連資源。 直至該資源被釋放回串連池,串連池才繼續分配它剩餘的4次機會。
當你在使用串連時可能應用到事務時,可以使用該模式的串連,以確定在事務進行期間您可以對該串連具有獨享許可權,以避免各個資料庫操作訪問的幹擾。
2.ConnLevel_High 優先順序-高
使用閒置實際串連分配串連資源,並且在該資源釋放回之前,該資源在串連池中將可能將其引用分配給其他申請者。*注意:此層級不保證在分配該資源後,仍然保持獨立佔有串連資源,若想獨立佔有資源請使用ReadOnely, 因為當串連池達到某一時機時該資源將被重複分配(引用記數)然而這個時機是不可預測的。如果您申請的串連會用於交易處理您可以使用ConnLevel_ReadOnly層級。
3.ConnLevel_None 優先順序-中
適當應用引用記數技術分配串連資源。
在該模式下,串連池內部會按照實際串連已經使用次數排序(多->少),然後在結果中選取 1/3 位置的串連資源返回。與優先順序-高相同該模式也不具備保持獨立佔有串連資源的特性。如果您申請的串連會用於交易處理您可以使用ConnLevel_ReadOnly層級。
4.ConnLevel_Bottom 優先順序-底
儘可能使用引用記數技術分配串連。在該模式下,串連池內部會按照實際串連已經使用次數排序(多->少),然後在結果中選取被使用最多的返回。該模式適合處理較為不重要的串連資源請求。與優先順序-高相同該模式也不具備保持獨立佔有串連資源的特性。如果您申請的串連會用於交易處理您可以使用ConnLevel_ReadOnly層級。
以上4條策略選自datebasepool_SDK(datebasepool是本文所開發的最終產物) 如何?交易處理
前面談到的都是關於使用資料庫連接進行普通的資料庫訪問。對於交易處理,情況就變得比較複雜。因為事務本身要求原子性的保證,此時就要求對於資料庫的操作符合"All-All-Nothing"原則,即要麼全部完成,要麼什麼都不做。如果簡單的採用上述的串連複用的策略,就會發生問題,因為沒有辦法控制屬於同一個事務的多個資料庫操作方法的動作,可能這些資料庫操作是在多個串連上進行的,並且這些串連可能被其他非事務方法複用。
Connection本身具有提供了對於事務的支援,具體實現方法請參看Connection的msdn,顯式的調用commit或者rollback方法來實現。但是要安全、高效的進行Connection進行複用,就必須提供相應的事務支援機制。我們採用的方法是:使用者以ConnLevel_ReadOnly模式申請一個串連之後該串連就由該申請者獨自享有該串連,具體事務操作由使用者自行設計編寫,串連池只提供使用者獨自佔有。 管理串連池
在上文中我們說過這個串連池內部串連管理使用的是獨立的線程來工作(threadCreate和threadCheck)threadCreate線程負責建立串連 ,threadCheck線程負責檢查每個串連是否達到自己的壽命,標誌串連壽命的條件是被引用的次數超過它最大被引用次數,或者達到最大存留時間。這些參數都由ConnStruct類管理,ConnStruct類是封裝串連的類,下面定義的就是串連池使用到的屬性變數(C#代碼) // 屬性
private int _realFormPool; // 串連池中存在的實際串連數(包含失效的串連)
private int _potentRealFormPool; // 串連池中存在的實際串連數(有效實際串連)
private int _spareRealFormPool; // 閒置實際串連
private int _useRealFormPool; // 已指派的實際串連
private int _readOnlyFormPool; // 串連池已經分配多少唯讀串連
private int _useFormPool; // 已經分配出去的串連數
private int _spareFormPool; // 目前可以提供的串連數
private int _maxConnection; // 最大串連數,最大可以建立的串連數目
private int _minConnection; // 最小串連數
private int _seepConnection; // 每次建立串連的串連數
private int _keepRealConnection; // 保留的實際空閑串連,以攻可能出現的ReadOnly使用,當空閑串連不足該數值時,串連池將建立seepConnection個串連
private int _exist = 20 ; // 每個串連生存期限 20分鐘
private int _maxRepeatDegree = 5 ; // 可以被重複使用次數(引用記數),當串連被重複分配該值所表示的次數時,該串連將不能被分配出去
// 當串連池的串連被分配盡時,串連池會在已經分配出去的串連中,重複分配串連(引用記數)。來緩解串連池壓力
private DateTime _startTime; // 服務啟動時間
private string _connString = null ; // 連接字串
private ConnTypeEnum _connType; // 串連池連線類型
private PoolState _ps; // 串連池狀態
// 內部對象
private ArrayList al_All = new ArrayList(); // 實際串連
private Hashtable hs_UseConn = new Hashtable(); // 正在使用的串連
private System.Timers.Timer time; // 監視器記時器
private Thread threadCreate; // 建立線程
private bool isThreadCheckRun = false ;
Java版本 // 屬性
private int _RealFormPool; // 串連池中存在的實際串連數(包含失效的串連)
private int _PotentRealFormPool; // 串連池中存在的實際串連數(有效實際串連)
private int _SpareRealFormPool; // 閒置實際串連
private int _UseRealFormPool; // 已指派的實際串連