非常老的話題 SQLSERVER連接池
寫這篇文章不是說要炒冷飯,因為園子里有非常非常多關於SQLSERVER連接池的文章,但是他們說的都是引用MSDN里的解釋
或者自己做一些測試試驗一下連接池的性能。但是對於SQLSERVER連接池的內部機制,好像都沒有非常清晰地說清楚。
連接池的作用就不說了,在園子里隨便搜一大堆
還有使用連接池有利也有弊,大部分文章都說連接池的好處沒有說連接池的不好的地方
連接池不好的地方在於事務的提交方面,如果上一個連接沒有提交事務,有可能下一個
連接會遇到因為上一個連接的事務沒有提交從而遇到意想不到的后果
詳細的可以自行GOOGLE
先說一下帶連接池功能的編程接口
微軟的SQLSERVER客戶端編程接口:
ADO和ADO.NET 都支持連接池這種機制
JAVA的JDBC也支持連接池這種機制
注意:連接池機制是客戶端數據庫驅動程序提供的,功能都在驅動程序里,所以JDBC跟ADO,ADO.NET的連接池功能會有區別
因為客戶端應用程序都是通過加載SQLSERVER的數據驅動控件做SQLSERVER連接。目前客戶端數據庫驅動程序主要有3種:
1、MDAC(微軟數據訪問組件) SQL2000的時候開始有的
2、SQLSERVER Native Client SQL2005開始引入的
3、Microsoft JDBC Provider 使用機制與MDAC和SQLSERVER Native Client不同
下面再說一下在論壇里經常有人問到的問題:
問題1:SQLServer連接池被創建在SQLClient端還是SQLServer端?
答案:在客戶端
當應用程序運行的時候,會有一個連接池的管理控件運行在應用程序的進程里,統一管理應用程序和SQLSERVER建立的所有連接,
並且維護這些連接一直處於活動狀態。當有用戶發出一個connection open指令時連接池會在自己維護的連接池中找一個處於空閑狀態
的連接放回自己管理的連接池里,給這個用戶使用。當用戶使用完畢后,發出connection close指令,連接池會把這個連接放回自己
管理的連接池里,讓他重新處於空閑狀態,而不是真的從SQL里登出。這樣如果下次有用戶需要相同連接,就可以重用這個連接,
而無須再去做物理連接了。就是說連接池是放在客戶端的,是客戶端機制
問題2:如果在一個應用程序里設置連接池的大小為40000個,第二個應用程序里設置連接池的大小也為40000個,
程序跟SQLSERVER會不會報錯?
答案:不會
當應用程序運行的時候,會有一個連接池的管理控件運行在應用程序的進程里,統一管理應用程序和SQLSERVER建立的所有連接,
並且維護這些連接一直處於活動狀態。當有用戶發出一個connection open指令時連接池會在自己維護的連接池中找一個處於空閑狀態
的連接放回自己管理的連接池里,給這個用戶使用。當用戶使用完畢后,發出connection close指令,連接池會把這個連接放回自己
管理的連接池里,讓他重新處於空閑狀態,而不是真的從SQL里登出。這樣如果下次有用戶需要相同連接,就可以重用這個連接,
而無須再去做物理連接了
這個問題是針對上面這段話的,如果一個應用程序指定了連接池的大小為40000個那么跟SQLSERVER的連接已經有40000個了,
SQLSERVER的最大連接數是32767,那么第二個應用程序再打開40000個連接會不會報錯呢?
本人根據園子里的這篇文章做了一個實驗測試會不會報錯,修改了他里面的一些代碼,下面是我自己寫的代碼跟報錯的內容跟文章鏈接
做實驗之前記得重啟一下SQLSERVER
親測SQLServer的最大連接數
http://www.cnblogs.com/wlb/archive/2012/04/08/2437617.html
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Data; 6 using System.Data.SqlClient; 7 8 namespace SQLServerMaxConnectionTest 9 { 10 class Program 11 { 12 static void Main(string[] args) 13 { 14 int maxCount = 40000; 15 string connstr="Server=joe;Database=AdventureWorks;User ID=sa;Password=test;pooling=true;connection lifetime=0;min pool size = 1;max pool size=40000"; 16 List<SqlConnection> collection = new List<SqlConnection>(); 17 for (int i = 0; i < maxCount; i++) 18 { 19 Console.WriteLine(string.Format("成功創建連接對象{0}",i)); 20 try 21 { 22 var db = new SqlConnection(connstr); 23 db.Open(); 24 collection.Add(db); 25 } 26 catch (Exception ex) 27 { 28 WriteErrLog.AppendErrLog(ex.ToString()); 29 } 30 31 } 32 } 33 } 34 }
1 2012-12-01 17:04:22 System.Data.SqlClient.SqlException: 當前命令發生了嚴重錯誤。應放棄任何可能產生的結果。 2 在 System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection) 3 在 System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj) 4 在 System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj) 5 在 System.Data.SqlClient.SqlInternalConnectionTds.CompleteLogin(Boolean enlistOK) 6 在 System.Data.SqlClient.SqlInternalConnectionTds.AttemptOneLogin(ServerInfo serverInfo, String newPassword, Boolean ignoreSniOpenTimeout, Int64 timerExpire, SqlConnection owningObject)
由於本人的筆記本性能不給力,在cmd里面打開的對象數達到16531 另一個是16124的時候差不多死機了,所以未能截圖。
16531+16124 差不多達到32767,測試結果表明,如果有程序一早打開了一定數目的連接,那么第二個應用程序就算
再打開40000個連接是沒有用的並會報錯,第二個應用程序能打開的連接數目等於SQLSERVER最大連接數減去第一個應用程序
打開的連接數
有一個問題非常奇怪:兩個應用程序都可以指定連接池的最大連接數為40000,如果是這樣的話應該SQLSERVER應該預先
保持了80000個活動連接,讓應用程序連接進來,這樣SQLSERVER應該會報錯,但是我用下面代碼測試了,發現沒有報錯
測試方法跟上面的那個測試一樣,做下面實驗之前請重啟一下SQLSERVER
這個問題已經有答案了,答案在文章的結尾o(∩_∩)o
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Data; 6 using System.Data.SqlClient; 7 8 namespace SQLServerMaxConnectionTest 9 { 10 class Program 11 { 12 static void Main(string[] args) 13 { 14 //改成500個連接 15 int maxCount = 500; 16 string connstr="Server=joe;Database=AdventureWorks;User ID=sa;Password=test;pooling=true;connection lifetime=0;min pool size = 1;max pool size=40000"; 17 List<SqlConnection> collection = new List<SqlConnection>(); 18 for (int i = 0; i < maxCount; i++) 19 { 20 Console.WriteLine(string.Format("成功創建連接對象{0}",i)); 21 try 22 { 23 var db = new SqlConnection(connstr); 24 db.Open(); 25 collection.Add(db); 26 } 27 catch (Exception ex) 28 { 29 WriteErrLog.AppendErrLog(ex.ToString()); 30 } 31 32 } 33 } 34 } 35 }
希望高人可以解答一下
問題3:連接池字符串里要不要寫pooling=true;connection lifetime=0;min pool size = 1;?
答案:個人覺得沒有必要寫,連接池默認是開啟的,連接的生命周期沒有必要指定,因為你都不知道
自己的SQL語句要運行多長時間,連接池的min pool size不需要指定了,只需要指定max pool size=32767
就可以了
問題4:sp_reset_connection 經常有人在論壇里問,使用SQL Trace的時候會有大量的sp_reset_connection
這個存儲過程的執行,這個存儲過程到底是什么來的?
網上對這個存儲過程的資料很少,MSDN也找不到資料
答案:我們知道,每個連接都會維護自己的一些獨有資源,比如臨時表,變量,游標等,
他也有可能會修改一些默認的設置,例如事務隔離級別等。如果不同用戶先后使用同一個連接,
會不會前一個用戶設置的狀態或申請的資源影響后一個用戶正常使用呢?如果連接池的管理者
不做特殊處理,的確會有這種情況發生。所以微軟的連接池技術里包括ADO.NET和ADO,
引入了一個特殊的指令:sp_reset_connection,來清除前一個用戶做的絕大多數設置,避免這種問題。
sp_reset_connection會在SQLSERVER里做些什么?
1、清除連接現有所有內部數據結構。包括:
(1)清除所有openxml打開的document句柄
(2)關閉所有的游標(cursor)
(3)釋放所有SQL語句句柄
(4)清除所有臨時對象(臨時表等)
(5)釋放連接持有的所有鎖
(6)清除緩存的所有安全上下文信息(security context)
2、重置連接設置。包括:
(1)重置連接的SQL Trace標志值(例如1204,1222,3604等)
(2)重置所有"SET" 選項值(SET IMPLICIT_TRANSACTIONS ON 等)
(3)重置連接的統計信息值
3、回滾所有SQLSERVER事務
需要說明的是,如果連接當前參與了一個由客戶端發起的分布式事務,這個分布式事務不會受到影響。
在SQL里的事務還會被保留
4、把當前數據庫切換到用戶默認數據庫
5、SQLSERVER會再次檢查當前用戶是否有權做數據庫連接。如果這個權力已被移除
SQLSERVER會中斷這個物理連接(這樣防止一個已經被取消訪問權的用戶還能長時間
使用數據庫的問題)
完成這些事情以后,一個連接基本上已經和他先前做的事情不再有任何關系(分布式事務除外)
可以說,這個連接和一個新的連接已經沒有什么大的區別了。通過這些,應用程序的用戶
可以盡快地拿到和釋放連接,而SQLSERVER這邊也不會因為連接的重復使用而產生相互影響的問題
准備下班了 有問題的童鞋可以發評論,希望大家拍磚o(∩_∩)o
剛才測試了一下,在SSMS里運行下面語句
1 SELECT [program_name] ,[spid] FROM [sys].[sysprocesses] WHERE [spid]>50
然后運行上面我給出的示例代碼程序跟SQLSERVER都在同一台電腦,改為500個連接那個,我雙擊打開了很多個cmd程序,
發現在cmd程序運行的時候會看到大量的連接在SSMS里面
當那些cmd程序全部運行完畢之后,這些進程就在SQLSERVER里消失
那么上面的問題:
兩個應用程序都可以指定連接池的最大連接數為40000,如果是這樣的話應該SQLSERVER應該預先
保持了80000個活動連接,讓應用程序連接進來,這樣SQLSERVER應該會報錯
答案:應該是連接池保持了80000個活動連接,SQLSERVER並沒有保持這些活動連接
想一想這也是正確的,如果客戶端指定了80000個連接,SQLSERVER就要一次打開
80000個連接(實際數量是32767個)並對這些連接進行維護,那么SQLSERVER肯定
慢得要命。因為客戶端的連接並沒有達到80000個那么多,你在連接字符串里指定80000個
連接,實際上只是指定連接池的連接數
客戶端的連接池和服務器端的線程池的區別
客戶端連接池即使是40000個,也只是一個設定值,並不是說當前程序打開了40000個長連接
即使真的有40000個連接到SQL Server,SQL Server同一時刻也只能保持32767個長連接
java:java語言制定了連接數據庫的標准JDBC,各種數據庫只需要按JDBC規范實現相應的驅動程序,JAVA程序通過JDBC API訪問不同類型數據庫
pgsql的JDBC驅動完全由java實現,沒有其他語言依賴庫,類似於python標准數據庫接口為python DB-API