SQL Server數據庫中,如果應用程序正在執行一個事務的時候突然遭遇了網絡異常,例如網絡掉包,網絡中斷等,那么這個事務會怎么樣? SQL Server數據庫是通過什么機制來判斷處理呢? 估計很多人跟我一樣都有不少疑問, 我們下面構造一個測試實驗來測試驗證一下。如下所示:
步驟1:在客戶端連使用SSMS工具連接到測試數據庫,執行下面腳本,顯性事務既不提交也不回滾。模擬事務正在執行當中。
USE AdventureWorks2012;
GO
SELECT@@SPID;
BEGINTRAN
DELETEFROM [dbo].[Products] WHERE ProductID=1;
--ROLLBACK;
輸出的會話ID為59
步驟2:在測試服務器上開啟Profiler跟蹤一下具體信息。具體步驟略過。
步驟3:通過VMware vSphere Client的控制台連接到測試服務器,禁用網卡,然后啟用網卡,模擬網絡異常。(注意:玩過Vmware的應該都知道,這里不詳細介紹!)
如下截圖所示,在跟蹤過程中,我們可以看到當我構造網絡異常時,會話ID=59的事務立即回滾了。
當然你也可以使用下面函數查看日志里面的相關記錄信息。如下所示:
SELECT*
FROMfn_dblog(NULL,NULL)
WHERE Operation ='LOP_ABORT_XACT';
你可以看到 LOP_BEGIN_XACT (事務開始)-> LOP_DELETE_ROWS (刪除記錄) -> LOP_INSERT_ROWS (插入記錄) ->LOP_ABORT_XACT (事務回滾)
通過上面實驗測試,我們知道當應用程序遭遇網絡異常時,數據庫會回滾未提交的事務。那么接下來的問題有下面幾個:
1: SQL Server需要多長時間才能檢測到會話的網絡異常?
如上所示,我斷開的是服務器的網絡,會話立即就回滾了。但是如果我斷開的是客戶端(執行SSMS客戶端的網絡),那么會話回滾的時間是30秒。如下截圖所示
事務開始時間為: 2017-07-27 13:48:01:820
事務回滾時間為: 2017-07-27 13:48:32.043
這個是服務器上Keep Alive參數控制的,具體位置 “SQL Server Configuration Manager”-> “SQL Server Network Configuration” -> "Protocol for MSSQLSERVER" -> "TCP/IP " 右鍵單擊屬性,如下截圖所示:
30000 的單位是毫秒, 等價於30秒, 如果你將這個設置為60000 ,那么測試結果就會是60秒或超過60秒。
當然這個時間差是你斷開網絡的時間和事務結束的時間差,而不是事務開始時間與結束時間差,如下測試所示,截圖1,由於需找到禁用網絡的位置,然后又切換窗口,導致延誤了幾秒,這個事務開始、結束時間差為70秒。 當然這個值不可能完全等於Keep Alive的值,因為還涉及參數Keep Alive Interval的值,所以這個值玩玩是大於等於Keep Alive的值。具體后面會講述!
2: SQL Server通過什么機制來判斷當前會話遭遇了網絡異常?
在這篇“ORACLE的Dead Connection Detection淺析”文章里面, 我介紹了Linux系統下TCP KeepAlive概念,顧名思義,TCP keepalive它是用來保持TCP連接的,注意它只適用於TCP連接。系統會替你維護一個timer,時間到了,就會向remote peer發送一個probe package,當然里面是沒有數據的,對方就會返回一個應答,這時你就知道這個通道保持正常。與TCP keepalive有關的三個參數tcp_keepalive_time、tcp_keepalive_intvl、tcp_keepalive_probes
/proc/sys/net/ipv4/tcp_keepalive_time 當keepalive起用的時候,TCP發送keepalive消息的頻度。默認是2小時。
/proc/sys/net/ipv4/tcp_keepalive_intvl 當探測沒有確認時,keepalive探測包的發送間隔。缺省是75秒。
/proc/sys/net/ipv4/tcp_keepalive_probes 如果對方不予應答,keepalive探測包的發送次數。缺省值是9。
其實在Windows系統中也有類似的參數,分別是KeepAliveTime、KeepAliveInterval、TcpMaxDataRetransmissions
KeepAliveTime 默認是7,200,000 milliseconds = 2 hours
KeepAliveInterval 默認是1,000 milliseconds = 1 second
TcpMaxDataRetransmissions 默認值是5次
https://technet.microsoft.com/en-us/library/cc957549.aspx
https://technet.microsoft.com/en-us/library/cc957548.aspx
https://technet.microsoft.com/en-us/library/cc938210.aspx
根據上面文檔描述,幾個值可以在HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters里設置,但是我檢查過Windows 2000/2003/2008/2012 默認情況下,在注冊表里面都沒有這個值,但是可以在注冊表里添加該選項。當然好像在有些操作系統下,有些參數是硬編碼值,有些還不能修改.“Important note: If OS is Windows Vista/2008, the number of TCP Keepalive attempts are hardcoded to 10 and could not be adjusted via the registry.” 。 具體參考下面鏈接,當然這些值個人沒有測試過。
那么SQL Server是否也是通過OS的這三個參數來判斷會話是否orphaned, 很顯然不是。它是通過SQL Server的Keep Alive、Keep Alive Interval來判斷會話是否遭遇網絡異常。那么操作系統的KeepAliveTime跟SQL Server的Keep Alive是不是一回事? 又有什么區別呢? 其實這個可以參考https://blogs.msdn.microsoft.com/apgcdsd/2011/05/02/sql-server-connection-keepalive-faq/。摘抄部分原文如下:
1、什么是SQL Server TCP連接的Keep Alive?
簡單說,Keep Alive 是SQL Server在建立每一個TCP 連接的時候,指定了TCP 協議的Keepaliveinterval 和 Keepalivetime參數。這樣對每個TCP連接,如果該連接空閑時間(沒有任何數據交互)超過Keepalivetime,TCP協議會自動發出Keepalive 包檢測連接存活與否。如果Keepalive檢測次數超過注冊表TcpMaxDataRetransmissions定義的值而對方還是沒有回應,那么TCP就認為該連接有問題而關閉它。通過這樣的機制SQL Server能夠檢測出Orphaned Connection等問題。
SQL Server 對每個TCP連接缺省指定Keep Alive 為30秒,Keepaliveinterval為1秒。Windows TCP配置的TcpMaxDataRetransmissions缺省是5次。就是說,如果TCP連接idle了30秒,那么TCP會發送第一個keepalive檢查。如果失敗,那么TCP會每隔1秒重發Keepalive 包,直到重發5次。如果第五次檢測依然失敗,則該連接就被Close。所以,一個TCP連接如果出現異常問題,大概在35秒的時候就會被Close。的機制SQL Server能夠檢測出Orphaned Connection等問題。
3、SQL Server的Keepalive 和Windows的TCP協議里面的Keepalive 是一樣的嗎?
原理一樣,但不相互干擾。Windows 的TCP協議也有keep alive 配置,位置如下:
HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters
OS的TCP協議的Keep Alive 和SQL Server 的Keep Alive 工作原理一樣的,就是在建立TCP連接的時候指定TCP連接的Keepalive屬性(參見后面描述)。但是SQL Server讀取自己注冊表的Keep Alive來設置TCP連接屬性,不理會Windows OS的注冊表里面的Keepalivetime和Keepaliveinterval的值。如果一個應用程序沒有顯式調用函數設置TCP連接的Keepalive屬性,那么他的TCP連接默認使用OS 的TCP配置。OSkeep Alive配置默認是關閉的。
參考資料:
https://blogs.technet.microsoft.com/nettracer/2010/06/03/things-that-you-may-want-to-know-about-tcp-keepalives/
https://blogs.msdn.microsoft.com/apgcdsd/2011/05/02/sql-server-connection-keepalive-faq/
https://blogs.msdn.microsoft.com/apgcdsd/2012/06/07/sql-server-connection-keep-alive-faq3/