SQL Server 连接问题案例解析(1)

      Microsoft Network Monitor(Netmon)是由微软发布的一款网络协议数据分析工具,利用Netmon可以捕获网络数据并进行查看和分析。在处理SQL Server 的连接问题时,Netmon常常会起到关键的作用。在本篇博文中,我将为大家分享一个通过使用 Netmon 解决的经典案例。

      在这个案例中,客户发现在客户端的 SQL Server Management Studio 中执行某一个Query时就会发生错误,错误信息是“connection forcibly close by the remote server ”。 为了调查连接被关闭的原因,我们在客户端和服务端抓取了Netmon。

      在正式分析这个案例前,我们先来介绍一些有关Netmon的知识和使用技巧。


                                                     Netmon界面

 

1. 在Netmon界面中的Frame Summary 部分,我们首先可以看到Frame Number,不管我们在浏览时是否有设置filter,Frame Number的值是不会发生改变的,它相当于Frame的一个行号。

2. 在左侧Network Conversation 中,我们会看到进程的name和ID,在示例中即为Ssms.exe和3352。继续展开后看到IPv4,那么我们可以知道这个conversation是从哪里来的。再次展开可以看到这个conversation的端口,本示例中,端口为1433到49428 。在这里需要额外讲解一下,当客户端程序创建连接到SQL Server时会使用哪些端口呢?客户端会向操作系统申请并使用一个动态端口并向SQL Server发送连接请求。如果在连接时使用的是machine name,provider默认会去连接1433端口,这是一个provider的行为,改变这个行为需要修改注册表:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSSQLServer\Client\<Provider>\tcp\DefaultPort

3. 当客户端尝试创建一个连接到SQL Server,有了source port和destination port后,在这两个port中就会形成一组physical tcp连接。形成连接之后每发一个包,包的sequence值就会发生变化。请注意,只有在同一个物理连接中,sequence的变化是连续的。如果客户端和SQL Server建立了两个不同的物理连接,这两个连接中的sequence没有任何关系。

4. Netmon中数据量很大,如果查看呢?

比如在浏览一个比较大的Netmon时,我们发现了一个Reset Flag:

21 3:46:20 PM 9/5/2014 21.8000442 Ssms.exe 172.22.204.237 172.16.221.38 TCP TCP: [Bad CheckSum]Flags=...A.R.., SrcPort=49428, DstPort=1433, PayloadLen=0,
Seq=3636257929, Ack=707503184, Win=0 {TCP:2,IPv4:1}

就可以从这个Frame的详细信息入手。从详细信息中可以找到它的source port是49428。此时我们可以就通过添加 filter Tcp.port==49428 来过滤出这个Reset的conversation。

另外一种过滤方法是直接在右键单击Frame后弹出的菜单中选择Find Conversation, 但这种方法有可能会造成丢包,因此还是推荐使用第一种方法。

找到了port就几乎相当于找到了出问题的连接。当然,对于不同时间点,同样的port有可能会是两个不同的连接,因为上一个连接关掉后,下一个连接有可能重用这个port。按照port过滤后,从reset 开始顺序往前看。

5. 在Netmon中我们还可以看到一些protocol是TDS的Frame。TDS的好处是在查看Frame Details时,可以看到更多的信息。例如我们查看一个TDS:SQLBatch的TDS Frame Detail,可以看到SQL的statement:

对于当SQL Server使用的端口不是默认的1433时,如何显示TDS frames,可以参考下面这篇博文:How to enable TDS parser to display TDS frames when SQLServer is listening on port other than default 1433

6. 除了在4中介绍的通过port进行过滤,最常用的filter还有
ipv4.address==<xxx.xx.xx.xx>

除直接在filter中写入之外还有一种添加filter的方法,以过滤出所有flag是reset的frames为例来说明:那么我可以在某个reset frame的details中找到这个flag,在右键单击弹出的菜单中选择Add Selected
Value to Display filter:

 
7. 一个SQL Server的包会在网络传输过程中会经过以下几层:

NIC(网卡物理设备)—NDIS(网卡驱动)—TCPIP (操作系统)—afd(操作系统后台线程,每一个tcp port上都会有一个afd)—SQL Server—for authentication(调用 sspi—lssas—DC)

Netmon所抓取的数据是在网卡驱动上面和TCPIP 下面的。所以Netmon所抓到的包是不能作为网卡真正发出去的包的,需要比较发出去的包和客户端收到的包来判断网卡或路由等是否进行了切包。

8. 在chimney开启时,抓到的包的信息有可能是不全的,如果在查看时发现包的行为很奇怪,怀疑丢包,那么一定要请客户关闭chimney(以管理员运行CMD并且执行命令:netsh interface tcp set global chimney=disabled)后重新收集Netmon。

 

      接下来,我们就来讨论一下今天的案例。当具备了以上Netmon的知识和技巧后,在处理这个案例中所收到的Netmon数据就非常有针对性了。将客户端的Netmon数据按照端口号filter后,可以很清楚的看到,客户端一直面对着重传的问题。16是12的重传,17也是12的重传,甚至18,19,20:

那么我们来看一下12的详细信息:

可以看到这个包的长度是4096。那么16呢?查看16的详细信息时我们发现,16的长度变成了1460:

 

       在重传时包的长度变小说明,由于之前大包无法传递,传递的包的大小被自动调小了。继续查看17,18,19和20会发现这几个包的长度都是1460。很明显,这是一个大包发不过去导致重传的问题。

      那么现在问题来了,为什么第一次大包发不过去,之后以小包重传也不成功呢?

      查看server端Netmon后会发现,这是由于传输4096的包被切成1460+1460+1172后,server端只收到了最后一个包。接下来,由于sequence断了,server端会认为这是一个不合法的包,因为中间的信息缺失了。之后这个连接就在server端直接被block了(所以我们收到的错误信息是“connection forcibly close by the remote server
”),因此后续re-transmit的1460的包server再也没有接收过(后续重传全都失败了)。

      最终问题的解决办法是关闭网卡上的两个选项:Jumbo Packet和Large Send Offload(LSO)。

 

 

       开启Jumbo Packet表示支持大包,关闭则表示从NIC发出的包采用标准大小1500。

      如果LSO是开启的,切包会由网卡驱动(NDIS)完成。如果LSO关闭,切包会由操作系统(TCP Stack)完成,切成多大由Jombo Packet的设置来决定。我们通常不建议由网卡来切包,因为网卡切包与网络环境有关,可能会导致包的大小不固定,建议还是由操作系统来进行切包。将这两个选项关闭后,传输包的大小均为标准大小1500,就解决了这个大包被切,重传失败的问题。

      这就是今天的分享,更多SQL Server案例学习请持续关注本博客的更新。