数据库镜像故障转移后,.NET应用程序连接SQL Server 超时(译文)

在本篇博客中,我将为大家解析一个有趣的现象。这个现象产生的起因是我的一个客户在数据库发生故障,数据库转移到镜像服务器上后,产生了连接超时错误。为了能够更好地理解这个问题,在我正式进行讲解之前,先来介绍一些有关数据库镜像的关键概念和术语。

为了提升数据库的可用性能力,SQL Server 在2005版中第一次引入了数据库镜像技术。它是一种软件解决方案,对硬件设施没有要求。在镜像功能中,对发生在主体数据库上的每一次插入、更新、删除操作,都要在镜像数据库上尽可能快地重新实现,我们将此行为称为“重做(Redo)”。重做的实现依赖于主体服务器向镜像服务器发送的一连串事务日志记录,镜像数据库会根据活跃的日志记录顺序,将所有操作重新执行一遍。当由于某些原因,主体数据库不可用时,我们可以设置成手动地或自动地转移使用镜像服务器上的数据库。

数据库镜像有三种操作模式:“高可用(high safety with automatic failover)”、“高保护(high safety without automatic failover)”和“高性能(High performace) ” 。为了支持自动故障转移功能,数据库镜像必须设置成“高可用”模式,同时必须配置一个“见证服务器”(Witness Server)。如果想要了解更多的SQL Server镜像技术内容,请阅读以下文章:

Database Mirroring in SQL Server 2005

https://technet.microsoft.com/zh-cn/library/cc917680.aspx

How to: Configure a Database Mirroring Session (SQL Server Management Studio)

https://msdn.microsoft.com/en-us/library/ms188712.aspx

接下来我们将详细讨论一下今天的问题。客户发现,在关闭了主本服务器后,应用程序会报超时错误。具体的场景如下:他们建立数据库镜像时配置了一个见证服务器,模式设置成高可用模式。在指定故障转移的镜像服务器的同时,在连接字符串中设置了连接超时参数为10s。在测试期间,当他们关闭了主体服务器上的SQL Server服务,故障转移功能够成功生效。然而,当他们将主体服务器关机或断开主体服务器网络时,故障转移仍能如期成功,即原镜像数据库成为新的主体数据库,但是应用程序尝试连接SQL Server镜像数据库时产生超时错误,错误信息如下:

Server Error in '/' Application.

A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: TCP Provider, error: 0 - The wait operation timed out.)

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.Data.SqlClient.SqlException: A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: TCP Provider, error: 0 - The wait operation timed out.)

我再重申一遍这个问题:如果客户只是停止了SQL Server服务,镜像能够正常工作。但当他们将主体服务器关机或断开网络,连接就会超时,应用程序无法如期连接到镜像数据库。

首先,我们要求客户增大连接超时时间参数,将其设置为60秒并再一次进行测试。该参数修改之后,该问题得到解决,即使将主体服务器关机,应用程序也能成功连接到镜像服务器。

至此,我们相当快地找到了解决问题的办法,但我们仍未具体了解究竟是什么原因导致了这个问题的发生?为什么增大了连接时间参数就解决了这个问题?为了找出答案,我们模拟了三种不同场景,并捕获了该场景下的网络信息。

场景一:

在只停止主体服务器上的SQL Server服务情形下,能够成功地从主体数据库自动切换使用镜像数据库。

1、在此场景下进行连接时,客户端首先发送SYN消息到主体服务器,然后主体服务器立即回复了ACK和RST消息
 
  

2、在客户端从主体服务器得到响应后,会立即尝试连接镜像服务器,并能够连接成功。

场景二:

在连接超时参数设置为10s的情况下,将主体服务器关机,查看故障转移情况。此场景下应用程序无法连接到SQL Server,并显示超时错误。

1、进行连接时,客户端首先发送SYN消息到主体服务器,但由于主体服务器已经关闭了,所以客户端没有从主体服务器返回任何信息。

2、因此,客户端会基于注册表中的TcpMaxConnectRetransmissions注册值重新发送一个SYN到主体服务器。TcpMaxConnectRetransmissions决定了一个未得到回应的连接中可以进行多少次TCP重传。其默认值为2。TCP会一直重传一个新的连接请求,直到该请求得到响应或该请求的重传次数到达了这个限制值。

TCP/IP会调整超时重传的频率。对于每个接口,原始传输与第一次重传之间的延迟是由TcpInitailRTT这一项的值决定的,默认是3s。每次尝试后,这个延迟时间会翻一倍。最后一次尝试后,TCP/IP等待双倍最后一次延迟的时间后,放弃此连接请求。

请注意,TcpMaxConnectRetransmissions与TcpMaxDataRetransmissions 是不同的,后者表示的是在一个现有的连接中TCP可以重传多少次未确认的数据段。

在网络的跟踪信息中,我们发现应用程序服务器在3s后重传了连接请求。TcpMaxConnectRetransmissions的设置为默认值2,因此,下一次连接请求的发送是6s之后,3s+6s=9s,即从开始连接到第二次重传连接请求一共花了9s的时间,在此之后,我们应该再等待6*2=12s的时间后才会尝试连接镜像数据库,而客户设置连接超时参数为10s。由于某些原因,我没有在网络信息中看到第二次重传信息,但不管如何,此连接已经超时了。在网络信息中,我们没有看到任何企图连接镜像服务器的行为。

场景三:

我们通过关闭主体服务器进行一次故障转移,但将连接超时参数由10s设置成60s。此场景下,应用程序能够成功连接到镜像数据库。

1、与上一场景的第一步一样,在进行连接时,客户端首先发送SYN消息到主体服务器,但由于主体服务器已经关闭了,所以客户端没有从主体服务器返回任何信息。

2、因此,客户端在3s之后向主体服务器重传了SYN消息。由于TcpMaxConnectRetransmissions的默认值为2,它应该在6s后尝试重新连接。然后再等待6*2=12s的时间后尝试连接镜像数据库。

3、由于某些原因 ,我们无法看到从客户端向主体服务器发送第二次重次SYN消息。然而,我们看到了在6+12=18s后,客户端向镜像服务器发送了SYN消息发起连接。此连接立即得到了响应,客户端与镜像服务器成功进行了连接。从起始时刻开始计算,共花费了3+6+12=21s的时间尝试连接到镜像数据库,而此时间值小于所设置的连接超时参数60s,这也是为什么这一次我们没有看到任何连接超时的信息。

在第一个场景中,我们之所以没有看到超时信息是因为即使SQL Server服务被停止了,但其服务器不是关闭的,在接到SYN消息后其服务器会立即回复一个RESET消息到客户端,因此,不需要花费10s的时间,应用程序服务器会直接向镜像数据库发送连接请求,故障转移功能能够如期成功。在第二种场景中,之所以会得到超时信息是因为主体服务器关闭掉了,因此应用程序服务器无法得到任何响应。又由于TcpMaxConnectRetransmissions的默认值为2,应用程序服务器必须要进行2次连接请求的重传后才能尝试连接镜像数据库。连接超时参数过小(用户设置为10s),导致在做连接请求重传时就超过了连接时限,报出超时错误,根本没有机会去尝试连接镜像数据库。

在第三种场景中,我们增大了连接超时参数,使得应用程序服务器在重传两次连接请求后,仍可以尝试连接镜像数据库,此时会成功连接到镜像数据库。

以下是与本文问题相关的更多参考信息:

How to: Configure a Database Mirroring Session (SQL Server Management Studio)

https://msdn.microsoft.com/en-us/library/ms188712.aspx

Making the Initial Connection to a Database Mirroring Session

https://msdn.microsoft.com/en-us/library/ms366348.aspx

Connection Retry Algorithm (for TCP/IP Connections)

https://msdn.microsoft.com/en-us/library/ms365783.aspx

TcpMaxConnectRetransmissions

https://technet.microsoft.com/en-us/library/cc758896(WS.10).aspx

TcpMaxDataRetransmissions

https://technet.microsoft.com/en-us/library/cc780586(WS.10).aspx