但是尚未从池中获取连接_SQLServer超时时间已到,但是尚未从池中获取连接
小編最近開發了一個項目,數據庫是SQLServer2008R2,在WinForm程序通過API接口短時間大批量上傳數據時,出現了錯誤”超時時間已到,但是尚未從池中獲取連接”,數據是一包一包上傳的,每次調用都對數據進行驗證,全部無誤之后才會插入數據庫。從網上得到的結果是所有池連接均在使用,并且達到了最大池大小,在connectionString中如果未指定max pool size的值,則max pool size=100,當訪問人員同時連接數據庫的數量為101人時,則等待SqlConnection.ConnectionTimeout設置的時間(默認是15 秒)后,還是沒有可用的Connection則會出現上面的錯誤。
Connection Pool 是什么呢 ?
每當程序需要讀寫數據庫的時候。Connection.Open()會使用ConnectionString連接到數據庫,數據庫會為程序建立 一個連接,并且保持打開狀態,此后程序就可以使用T-SQL語句來查詢/更新數據庫。當執行到Connection.Close()后,數據庫就會關閉當 前的連接。很好,一切看上去都是如此有條不紊。但是如果我的程序需要不定時的打開和關閉連接,(比如說 ASP.Net 或是 Web Service ),例如當Http Request發送到服務器的時候,我們需要打開Connection 然后使用Select* from Table 返回一個DataTable/DataSet給客戶端/瀏覽器,然后關閉當前的Connection。那每次都Open/Close Connection 如此的頻繁操作對于整個系統無疑就成了一種浪費。
ADO.Net Team就給出了一個比較好地解決方法。將先前的Connection保存起來,當下一次需要打開連接的時候就將先前的Connection 交給下一個連接。這就是Connection Pool。
Connection Pool 如何工作的?
首先當一個程序執行Connection.open()時候,ADO.net就需要判斷,此連接是否支持Connection Pool (Pooling 默認為True),如果指定為False, ADO.net就與數據庫之間創建一個連接(為了避免混淆,所有數據庫中的連接,都使用”連接”描述),然后返回給程序。 如果指定為 True,ADO.net就會根據ConnectString創建一個Connection Pool,然后向Connection Pool中填充Connection(所有.net程序中的連接,都使用”Connection”描述)。填充多少個Connection由Min Pool Size (默認為0)屬性來決定。例如如果指定為5,則ADO.net會一次與SQL數據庫之間打開5個連接,然后將4個Connection,保存在 Connection Pool中,1個Connection返回給程序。
當程序執行到Connection.close() 的時候。如果Pooling 為True,ADO.net 就把當前的Connection放到Connection Pool并且保持與數據庫之間的連接。 同時還會判斷Connection Lifetime(默認為0)屬性,0代表無限大,如果Connection存在的時間超過了Connection LifeTime,ADO.net就會關閉的Connection同時斷開與數據庫的連接,而不是重新保存到Connection Pool中。 (這個設置主要用于群集的SQL 數據庫中,達到負載平衡的目的)。如果Pooling指定為False,則直接斷開與數據庫之間的連接。
然后當下一次Connection.Open() 執行的時候,ADO.Net就會判斷新的ConnectionString與之前保存在Connection Pool中的Connection的connectionString是否一致。ADO.Net會將ConnectionString轉成二進制流,所以也就是說,新的ConnectionString與保存在Connection Pool中的Connection的ConnectionString必須完全一致,即使多加了一個空格,或是修改了Connection String中某些屬性的次序都會讓ADO.Net認為這是一個新的連接,而重新創建一個新的連接。所以如果您使用的UserID,Password的認 證方式,修改了Password也會導致一個Connection,如果使用的是SQL的集成認證,就需要保存兩個連接使用的是同一個)。 然后 ADO.net需要判斷當前的Connection Pool中是否有可以使用的Connection(沒有被其他程序所占用),如果沒有的話,ADO.net就需要判斷ConnectionString設 置的Max Pool Size (默認為100),如果Connection Pool中的所有Connection沒有達到Max Pool Size,ADO.net則會再次連接數據庫,創建一個連接,然后將Connection返回給程序。 如果已經達到了 MaxPoolSize,ADO.net就不會再次創建任何新的連接,而是等待Connection Pool中被其他程序所占用的Connection釋放,這個等待時間受SqlConnection.ConnectionTimeout(默認是15 秒)限制,也就是說如果時間超過了15秒,SqlConnection就會拋出超時錯誤(所以有時候如果SqlConnection.open()方法拋 出超時錯誤,一個可能的原因就是沒有及時將之前的Connnection關閉,同時Connection Pool數量達到了MaxPoolSize。) 如果有可用的Connection,從Connection Pool 取出的Connection也不是直接就返回給程序,ADO.net還需要檢查ConnectionString的ConnectionReset屬性 (默認為True)是否需要對Connection 最一次reset。這是由于,之前從程序中返回的Connection可能已經被修改過,比如說使用 SqlConnection.ChangeDatabase method 修改當前的連接,此時返回的Connection可能就已經不是連接當前的Connection String指定的Initial Catalog數據庫了,所以需要reset一次當前的連接。
解決方法
1. 修改幾個關鍵頁面或訪問比較頻繁的數據庫訪問操作,使用DataAdapter和DataSet來獲取數據庫數據,不要使用DataReader。
DataReader是獨占連接的,每個DataReader都要占用一個連接。當然這個情況是偶爾出現的,所以會很長時間出現一次,因為只有同時有超過連接池最大連接數量的并發操作才會發生。比如說最大連接設100,假設有100個人同時使用DataReader正在讀取數據庫內容,那么當第101人讀取的時候,連接池中的連接已經沒有了,就會出現上面的錯誤。而且你加大并發數量只能暫時緩解問題,如果你加大到200個并發連接,如果有201人同時操作怎么辦?你說了你使用Connection對象的Close()方法,這是不行的,因為Close()方法僅僅是關閉連接,但這個連接沒有釋放,還是被這個對象占用,要釋放必須使用Connection的Dispose()方法顯式釋放連接才可以,否則這個對象占用的連接只能等到垃圾收集的情況下才能被釋放。這種情況肯定會出現“超時時間已到”的錯誤。
2. 在訪問數據庫的頁面上使用數據緩存,如果頁面的數據不是經常更新(幾分鐘更新一次)的話,使用Cache對象可以不用訪問數據庫而使用緩存中的內容,那么可以大大減少連接數量。
3. 修改代碼,把使用Connection對象的地方都在Close()后面加上Dispose()調用。
4. 建議對數據庫操作進行大的修改,建立自己的數據庫操作代理類,繼承System.IDisposable接口,強迫釋放資源,這樣就不會出現連接數量不夠的問題了。
總結
以上是生活随笔為你收集整理的但是尚未从池中获取连接_SQLServer超时时间已到,但是尚未从池中获取连接的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 为什么冲马桶前,一定要先盖盖子?
- 下一篇: excel图表交互联动_如何使用高大上的