轉載:http://blogs.msdn.com/b/apgcdsd/archive/2012/06/07/sql-server-connection-keep-alive-faq-3.aspx
這個是SQL Server Keep Alive FAQ文章的最後一篇。
問題1:為什麼我在客戶端設定了KeepAlive值,但是我使用SQL server management studio 測試的時候,該值總是不起作用?
[答]目前市場上主流的資料庫訪問技術有ODBC,OLEDB,SQL native client 和 SQLclient。其中SQLclient是.NET裡面使用的託管provider。目前SQL Server configuration manager裡面提供的keepalive設定是針對SQL native client 的:
也就是說,該值只對使用SQL Native Client 來訪問資料庫的程式有效。SQLcmd.exe是使用SQL native client來訪問SQL server的一個命令列工具,所以您可以使用SQLCmd來測試上面的keepalive值。注意修改keepalive值後,你需要退出SQLcmd重新執行它才能使用新值。我在幾組機器測試了幾組不同的keepalive值,SQLcmd程式的TCP連線都很好按照設定的值發出keepalive網路包。注意你需要使用TCP連線伺服器來測試。我使用下面的連線串以保證使用TCP 連線:
SQLcmd -E -STCP:mycomputer\SQL2012,55890
(機器名:mycomputer,例項名:SQL2012,埠號 55890)
我使用network monitor 3.4捕獲keepalive包如下圖:
上圖中深藍高亮的是我挑出的由伺服器TCP連線發給SQLcmd的keepalive包,每個包間隔30秒(時間間隔可以看time offset前後相減即可)。而上圖間隔7秒的是SQLcmd的TCP連線發出的keepalive包。我測試中使用SQL server configuration manager設定了native client 的keepalive為7秒。從上面圖也印證了伺服器端的TCP連線和客戶端的TCP連線各自傳送自己的keepalive包,相互不影響。
注意, SQL Server native client的keepalive值存放在下面登錄檔裡面:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSSQLServer\Client\SNIx.0\tcp\Property2
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSSQLServer\Client\SNIx.0\tcp\Property3
那麼SQL server management studio (ssms.exe)是使用.NET managed的SQLclient,所以上面針對native client的設定對SQLclient無效。SQL server management studio 使用了 hard-coded的30秒值,無法修改。
問題2. ODBC,SQL OLEDB,SQL native client和SQLClient的TCP keepalive預設時間都是多少?在哪裡可以設定?
[答]預設值都是30秒。
可以修改如下注冊表值來設定ODBC 和SQL OLEDB的keepalive值:
HKLM\SOFTWARE\Microsoft\MSSQLServer\Client\SuperSocketNetLib\Tcp ==> KeepAliveTime : DWORD : <dwValue>
HKLM\SOFTWARE\Microsoft\MSSQLServer\Client\SuperSocketNetLib\Tcp ==> KeepAliveInterval : DWORD : <dwValue>
注意如果是x64的機器,在上面執行32位的ODBC/SQL OLEDB程式,則需要修改如下注冊表:
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\MSSQLServer\Client\SuperSocketNetLib\TCP
對於SQLserver native client 和SQL server 的keep alive設定,可以在SQL server configuration manager 修改,具體請參考請參考FAQ (1)和FAQ (2)或問題1。
對於SQL client,需要在使用sqlclient的應用程式裡面使用程式碼設定。
問題3:伺服器端如果網線被拔掉,這對於那些已經存在的TCP連線會有什麼影響?SQL server會馬上關閉連線麼?如果拔掉網線然後馬上又插回去呢?
[答]這樣問題複雜一些。當網線拔掉的時候,網路卡首先獲知該事件。Windows 2003以及後面的作業系統都實現了media sense 媒體感知功能。操作系 收到網路卡發來的media sense 的斷線(disconnect)事件後,如果在DisconnectDampInterval(一般是5-10秒)的時間內,網線連線還沒有恢復,那麼就會傳送資訊給偵聽該網路卡IP的連線。那麼所有監聽該IP的上層應用程式如SQL server 就會得到disconnect的事件,然後就會進行相應的動作如kill connection, rollback事務等。
具體點來說,SQL server 的網路IO是透過IOCP (IOCompletionPort)實現的。 SQL server在後臺有幾個專門的執行緒偵聽該埠。這些執行緒呼叫GetQueuedCompletionStatus()函式得到網路資料狀態,然後呼叫相應的callback函式(如ReadHandler函式)來做具體的資料處理。當網線斷的時候,作業系統過了DisconnectDampInterval時間後,GetQueuedCompletionStatus函式就會得到disconnect資訊,而callback函式readhandler就被呼叫來讀取資料但實際上是讀到disconnect的網路斷事件。SQL server 得知該disconnect事件後,就生成一個task,該task就是做kill session的任務,把事務會滾,把連線(connection)關掉。
所以說,如果你在很快的時間內(幾秒),拔掉網線馬上插回來,那麼SQL server的TCP連線(connection)會保持不會被關閉。
你或許會問,media sense功能如何disable呢?具體可以參考如下命令:
netsh interface ipv4 set global dhcpmediasense=disabled
netsh interface ipv6 set global dhcpmediasense=disabled
如何察看這些設定的命令如下:
netsh interface ipv4 show global
netsh interface ipv6 show global
回到keepalive。如果SQL Server伺服器的TCP的keepalive設定時間很短,小於DisconnectDampInterval -5時間值,那麼SQL server會先於作業系統得知連線斷線的狀態而採取相應的動作。實際中不建議過短的keepalive值。
問題4:DisconnectDampInterval在哪裡可以修改?有沒有media sense的更多的資訊?
[答]可以參考如下WinCE的文件:在
http://msdn.microsoft.com/en-US/library/ee486075(v=WinEmbedded.60).aspx
ConnectDampingInterval specifies the amount of time, in seconds, by which TCP/IP will delay any action in response to a Media Sense connect event.
DisconnectDampInterval specifies the amount of time, in seconds, by which TCP/IP will delay any action in response to a Media Sense disconnect event.
在非CE平臺上該定義依然有效,不過,預設值是5~10秒。可以修改如下注冊表修改該值:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\Tcpip\Parameters
DisconnectDampInterval
ConnectDampInterval
這兩個引數有最大最小值限制, Min = 5 seconds,Max = 10 seconds。
問題5:如下圖SQL server的網路協議TCP的屬性頁為何沒有keep alive interval選項?
[答]SQL server的網路協議TCP的keep alive interval是不能修改的,登錄檔也無法修改. SQL server原始碼裡面寫定了1秒的時間間隔.
問題6:我的應用程式如何才能捕獲keepalive機制偵測到的網路問題?
[答] 對於使用者程式,你需要呼叫ExecuteSQL或類似的方法才能獲知connection的真實狀態. 如下面例子所示:
using (SqlConnection connection = new SqlConnection())
{
connection.ConnectionString ="Data Source=TCP:cn-simon-w7\\sql2012,55890;Initial Catalog=test;Integrated Security=true;pooling=false";
SqlCommand cmd = new SqlCommand();
cmd.CommandText = "select * from t";
cmd.Connection = connection;
cmd.CommandTimeout = 0;
try
{
connection.Open();
cmd.ExecuteNonQuery();
}
catch (Exception ex)
{
//do your work here for this exception
MessageBox.Show(ex.Message);
};
}
我做了小測試步驟如下:
1) 想辦法阻塞 "select * from t"使得它不返回結果而處於等待狀態。
2) 然後拔掉伺服器網路線。
3) 然後過一會,我的catch語句捕獲了錯誤:
你其實可以對ex物件做更多的處理,請參看有關程式設計文件,此處不多描述。