开发者一直在找那个位置:某条连接字符串、某个 JDBC URL 或某条 tnsnames.ora 记录在里面选择 VPN。这样的位置并不存在。隧道由操作系统或注册虚拟网卡的厂商客户端建立,其上每一层数据库库都使用由此产生的套接字层。连接字符串只说明主机、端口和凭据,这就是它的全部词汇。

误区:连接字符串可以选择隧道

驱动向主机和端口打开 TCP 套接字。它没有建立隧道的 API,没有隧道配置文件的字段,也没有向 VPN 网关认证的手段。

这就是为什么一旦路由存在,各种工具的行为完全一致。SSMS、DBeaver、Eclipse Data Tools 和普通的 sqlcmd 都表现得像服务器在本地网络上一样,因为从套接字的角度看确实如此。虚拟网卡让数据库子网变得可达,而驱动从不知道这是怎么做到的。

这也解释了为什么连接字符串修不好掉线的隧道。链路断开时,驱动报告超时或一般网络错误,而把它当作查询问题的应用代码会永远重试错误的那一层。

在驱动运行之前先把隧道建起来

按照服务商或网络团队的说明,在操作系统层面连接 VPN,然后再打开工具。实践中这意味着在驱动解析主机名之前,通往数据库子网的路由就已经存在。

把隧道的监护放在应用之外. 由 PHP 或 Java 服务自己去建链路,等于把某家服务商的行为写进业务代码,每次故障切换都变成一次应用发版。而由监护程序持有隧道、监视它并重启它,是与查询路径分开的另一件事。

在责怪驱动之前先检查路由。如果主机名解析不了,或者从 shell 里端口不应答,任何连接字符串都改变不了这一点。

误区:Windows 认证失败意味着路由坏了

跨 VPN 的 Windows 认证经常失败,报错看起来像网络故障,而路由其实是好的。当账户属于另一个域、笔记本又加不进去时,请用显式凭据启动客户端,而不是用桌面会话的身份。

runas /noprofile /netonly /user:CORP\dbreader "C:\Program Files (x86)\Microsoft SQL Server Management Studio 18\Common7\IDE\Ssms.exe"

/netonly 开关只把这些凭据用于远程认证。用 SELECT SUSER_SNAME() 确认服务器实际收到的是什么,而不要相信连接对话框。据说把服务器地址的凭据存进 Windows 凭据管理器对 SSMS 有效,但对以另一个账户运行的服务并不总是有效,所以在更改登录方式之前先确认进程用的是哪个身份。

延迟会把服务器端游标变成超时

服务器端游标让数据库保留结果并逐步取回,这把一个大结果变成许多小的往返。在局域网里这很便宜。经过一条增加几十毫秒延迟的隧道时,同一条查询可能在几乎没传多少数据的情况下就超过命令超时。

改用客户端游标,或者调大取数大小并把行分批,往返次数就会下降。瓶颈是交换次数,而不是链路标称速度,所以测吞吐量会误导你,测往返次数则不会。要用用户实际拉取的最大结果集来测,而不是用立刻返回的单行查询。如果超时只是位置移动而没有消失,就同时调大命令超时和取数大小再测一次。

同样的算术适用于话多的 ORM,以及任何按行发一条语句的客户端。如果查询计划没问题、传输量也小,那么在调数据库之前先数一数网络交换次数。

检查端口、实例和本地过滤

命名实例监听动态端口,所以要确认端口,而不是假定 1433。对默认实例,该值可以从该实例 SuperSocketNetLib 键下的 TcpPort 注册表值读出。对 Oracle,要确认 tnsnames.ora 里的主机名能解析、地址有应答、监听端口被允许;1521 只是常见默认值,不是保证。

然后要排除隧道自身的访问列表,最后再排除本机杀毒软件或主机防火墙——即使客户端和服务器是同一台机器,它们也可能拦住连接。在改配置之前先记下失败发生在哪一步,因为名称解析、TCP 可达性、以及 TLS 或登录,各自会用不同的报错失败。

把这一步写进工单。否则下一个人会重走一遍当初引发这一切的连接字符串搜索。

相关文章