如何在香港服务器运行 Windows Server 时,把 RDMA 和 SQL AlwaysOn 结合起来,压低金融交易库的响应延迟

夜盘刚收,我站在香港葵涌那排机柜前,盯着监控墙上那条“撮合库 P99 写入延迟”曲线——行情放量时总会偶尔抬头。白天的交易窗口不允许试错,我只有这一夜的维护窗把延迟再压一截。
这篇文章是那一夜到上线后的完整记录:硬件选型、RDMA(SMB Direct)的布线与配置、Windows/交换机的 DCB/QoS、SQL Server AlwaysOn 可用性组的搭建、压测与指标对比、以及几个在现场踩过的坑。它不止是“怎么做”,也包含“为什么这么做”。
目标与边界
目标:在不改变业务 SQL 语义的前提下,降低交易库的写入与同步路径延迟,稳定早盘/尾盘放量时的 P99/P99.9。
核心手段:
- 利用 RDMA(RoCEv2)+ SMB Direct 加速备份/还原、自动播种(direct seeding 辅助场景)、日志/快照落盘到 SOFS(Scale-Out File Server)等基于 SMB 的数据流;
- SQL Server AlwaysOn 可用性组(AG) 做高可用/读写分离:同城(机房内)同步提交 + 自动故障转移;异地(可选)异步提交作为只读灾备;
- 把数据平面与管理/客户端平面彻底隔离,对 RDMA 通道做 DCB(PFC/ETS)与队列调度,保证流量无损、低抖动。
非常重要的边界认知:AG 的数据复制走的是数据库镜像端点(TCP/5022),不是 RDMA。
我们把 RDMA 用在 SMB3/SMB Direct 的链路上(例如自动播种文件、备份/还原到 SOFS、S2D/CSV、日志落盘到远端文件服务器等),以及剥离 AG 之外的重 I/O 流。这能明显减少 CPU 抢占与网络抖动,缩短“总体”收敛时间,间接改善峰值时段的写入等待与 redo 队列。
现场硬件与拓扑
服务器与网络(实配示例)
| 角色 | 型号/CPU | 内存 | 磁盘 | NIC(RDMA) | OS | SQL |
|---|---|---|---|---|---|---|
| HKSQL1/2(主/同步副本) | Dell R650, Xeon Gold 6338N(32C) | 512 GB | 8× NVMe PM1733 3.84TB(RAID10 for data, RAID1 for log) | Mellanox ConnectX-5 25GbE ×2(RoCE v2)+ 10GbE 管理口 | Windows Server 2022 Datacenter | SQL Server 2022 Enterprise |
| HKSQL3(只读/异步灾备) | 同上 | 512 GB | 同上 | 同上 | 同上 | 同上 |
| SOFS01/02(文件服务器集群,S2D) | Dell R650 | 512 GB | 20× NVMe + 2× SSD Cache(S2D) | ConnectX-5 25GbE ×2 | Windows Server 2022 Datacenter | FS-Role |
交换机:叶-脊两层,Arista 7050X3(25/100GbE),开启 DCB(PFC on 3)、ETS,全链路 MTU 9216。
布线:
- RDMA/SMB 网络(VLAN 200):HKSQL1/2/3 与 SOFS01/02 双上联到叶交换,LACP 聚合;
- 客户端/业务网络(VLAN 10):交易应用与 AG Listener;
- 管理网络(VLAN 99):BMC、域控、跳板等。
不要让 SMB/AG/管理混杂一条线,这会在放量时出现“看不见”的队列争用。
设计要点(为什么这么搭)
- AG 同步提交只用于机柜内的 HKSQL1/2:保证毫秒级 RTT;
- 异步副本 HKSQL3 面向报表/风控,只读;
- 备份/播种/大体量文件流 走 SMB Direct(RDMA) 到 SOFS:释放 SQL 进程 CPU,缩短维护窗;
- QoS/ETS 给 SMB(优先级 3)保底带宽,PFC 保证无丢包,RDMA 零拷贝减少延迟抖动;
- Jumbo Frame 端到端一致,避免 MTU 不对齐导致的隐形重传;
- Listener 与 DNS 定位 固定在客户端网络,避免客户端误打到 RDMA 平面。
步骤一:Windows 与 NIC 的 RDMA/队列配置
以下以 Mellanox ConnectX-5 + Windows Server 2022 为例。请根据驱动版本适当调整属性名。
1. 打开 RDMA、多队列、巨帧(两台 SQL、两台 SOFS 全都做)
# 查看并启用 RDMA
Get-NetAdapterRdma
Enable-NetAdapterRdma -Name "RDMA1","RDMA2"
# 打开 RSS / 动态中断
Enable-NetAdapterRss -Name "RDMA1","RDMA2"
# 统一 MTU(先从交换机到服务器端口统一 9216)
# Mellanox 驱动通常通过高级属性 *JumboPacket
Set-NetAdapterAdvancedProperty -Name "RDMA1" -RegistryKeyword "*JumboPacket" -RegistryValue 9014
Set-NetAdapterAdvancedProperty -Name "RDMA2" -RegistryKeyword "*JumboPacket" -RegistryValue 9014
# 为 Mellanox 设定 RoCE v2(若默认 v1)
Set-NetAdapterAdvancedProperty -Name "RDMA1" -DisplayName "RoCE mode" -DisplayValue "RoCE v2"
Set-NetAdapterAdvancedProperty -Name "RDMA2" -DisplayName "RoCE mode" -DisplayValue "RoCE v2"
2. DCB/QoS(PFC + ETS)
# 开启 NIC 级 QoS/DCB
Enable-NetAdapterQos -Name "RDMA1","RDMA2"
# 为 SMB Direct 指定 802.1p 优先级 3(NetDirect = 445)
New-NetQosPolicy -Name "SMB-Direct" -NetDirectPortMatchCondition 445 -PriorityValue8021Action 3
# 启用 PFC:只对优先级 3 开;其它优先级关掉,避免“全链路暂停风暴”
Enable-NetQosFlowControl -Priority 3
Disable-NetQosFlowControl -Priority 0,1,2,4,5,6,7
# ETS:保证 SMB 至少 60% 带宽(根据业务占比调整)
New-NetQosTrafficClass -Name "SMB" -Priority 3 -BandwidthPercentage 60 -Algorithm ETS
交换机侧(示意,Arista 风格)
dcbx application priority ethernet 3 smb
priority-flow-control priority 3 enable
priority-flow-control no-drop
priority-group 3 bandwidth 60 pfc on
interface Ethernet1-32
mtu 9216
priority-flow-control on
spanning-tree portfast
关键点:端到端一致——服务器/交换机的 PFC、ETS、MTU 必须闭环一致,否则 RDMA 要么不上路,要么不稳定。
3. SMB Multichannel/Direct 检查
# 确认 SMB Multichannel 与 Direct 已启用(默认启用)
Set-SmbServerConfiguration -EnableSMBMultichannel $true -Force
Set-SmbClientConfiguration -EnableMultiChannel $true
# 约束 SMB 只走 RDMA 接口(可选,但强烈建议)
# 先取 RDMA 网卡的 InterfaceIndex
Get-NetAdapter | ft Name,InterfaceDescription,InterfaceIndex
New-SmbMultichannelConstraint -InterfaceIndex 12,13 # RDMA1/2 的 Index
验证 RDMA 是否真正生效(两端都看):
# 在客户端(SQL 服务器)上连到 SOFS 后查看多通道连接
Get-SmbMultichannelConnection | ft ServerName,ClientInterfaceIndex,Rdma,ClientRSS,ServerRSS,State
# 网络接口能力
Get-SmbClientNetworkInterface | ft InterfaceIndex,RdmaCapable,RssCapable,LinkSpeed
看到 Rdma = True 才算成功;否则多半是 PFC/ETS/MTU/路径不一致导致的“看起来通了,实际上没走 RDMA”。
步骤二:Windows 集群与 SOFS(SMB Direct 的落脚点)
域控/证书/时间:两台 SQL、两台 SOFS 全部入域,时间同步;
创建 WSFC:把 SOFS01/02 先组为一个 文件服务器集群(Scale-Out File Server),导出共享:
\\sofs\sqlbackups(备份/还原/日志归档)
\\sofs\seeding(大库首播种临时文件)
共享权限:SQL Server 服务账号(域账号)授予 Full Control;SMB 高可用由 SOFS 提供,SMB 客户端(SQL)自动多通道 + RDMA。
步骤三:SQL Server AlwaysOn 可用性组
前提:三台 SQL 已安装 SQL Server 2022 Enterprise,同一域,启用 Windows Server Failover Clustering 功能;数据库 TradingDB 已就绪。
1. 打开 HADR、端点与防火墙
-- 三台都执行
EXEC sys.sp_configure N'contained database authentication', 1;
RECONFIGURE WITH OVERRIDE;
-- HADR 端点(TCP 5022)
CREATE ENDPOINT [Hadr_endpoint]
STATE=STARTED
AS TCP (LISTENER_PORT = 5022)
FOR DATABASE_MIRRORING (ROLE = ALL, ENCRYPTION = REQUIRED, ALGORITHM = AES);
GO
# Windows 防火墙(入站 5022、出入 445、客户端 1433)
New-NetFirewallRule -DisplayName "SQL HADR 5022" -Direction Inbound -Protocol TCP -LocalPort 5022 -Action Allow
New-NetFirewallRule -DisplayName "SMB 445" -Direction Inbound -Protocol TCP -LocalPort 445 -Action Allow
New-NetFirewallRule -DisplayName "SQL 1433" -Direction Inbound -Protocol TCP -LocalPort 1433 -Action Allow
在域环境里,AG 端点通常用 Windows 身份验证(服务账号),避免证书错配的问题。
2. (可选)利用 SMB Direct 做“首播种”/大库迁移
-- 主库上先备份到 SOFS(走 RDMA 的 SMB)
BACKUP DATABASE [TradingDB]
TO DISK = N'\\sofs\seeding\TradingDB_full.bak'
WITH COMPRESSION, CHECKSUM, STATS=5;
-- 备库上从 SOFS 还原到 NORECOVERY
RESTORE DATABASE [TradingDB]
FROM DISK = N'\\sofs\seeding\TradingDB_full.bak'
WITH NORECOVERY, REPLACE, STATS=5;
为什么要用 SMB Direct?
因为 TB 级的库用传统 TCP 走管理/业务网络,CPU 与瓶颈会很明显;RDMA 的 SMB 能把这段“迁移/播种”时间缩到极短,并且不和交易侧 TCP 冲突。
3. 创建 AG(HKSQL1 主,HKSQL2 同步自动切换,HKSQL3 异步只读)
-- 在主副本(HKSQL1)上
CREATE AVAILABILITY GROUP [AG_Trading]
WITH (DB_FAILOVER = ON, CLUSTER_TYPE = WSFC, AUTOMATED_BACKUP_PREFERENCE = SECONDARY)
FOR DATABASE [TradingDB]
REPLICA ON
N'HKSQL1' WITH (
ENDPOINT_URL = 'TCP://hksql1.corp.local:5022',
AVAILABILITY_MODE = SYNCHRONOUS_COMMIT,
FAILOVER_MODE = AUTOMATIC,
SEEDING_MODE = MANUAL, -- 我们已用 SMB 预播种;也可改 AUTOMATIC
SECONDARY_ROLE (ALLOW_CONNECTIONS = READ_ONLY)
),
N'HKSQL2' WITH (
ENDPOINT_URL = 'TCP://hksql2.corp.local:5022',
AVAILABILITY_MODE = SYNCHRONOUS_COMMIT,
FAILOVER_MODE = AUTOMATIC,
SEEDING_MODE = MANUAL,
SECONDARY_ROLE (ALLOW_CONNECTIONS = READ_ONLY)
),
N'HKSQL3' WITH (
ENDPOINT_URL = 'TCP://hksql3.corp.local:5022',
AVAILABILITY_MODE = ASYNCHRONOUS_COMMIT,
FAILOVER_MODE = MANUAL,
SEEDING_MODE = MANUAL,
SECONDARY_ROLE (ALLOW_CONNECTIONS = READ_ONLY)
);
GO
在 HKSQL2/3 上加入 AG:
ALTER AVAILABILITY GROUP [AG_Trading] JOIN;
ALTER DATABASE [TradingDB] SET HADR AVAILABILITY GROUP = [AG_Trading];
GO
创建 Listener(客户端网络):
ALTER AVAILABILITY GROUP [AG_Trading]
ADD LISTENER N'AG-TRADING'
( WITH IP ((N'10.10.10.50', N'255.255.255.0')), PORT=1433 );
GO
提交模式选择:
- HKSQL1↔HKSQL2:SYNCHRONOUS_COMMIT + AUTOMATIC FAILOVER(同机柜、低 RTT)
- HKSQL3:ASYNCHRONOUS_COMMIT(远端/只读,避免影响主事务提交)
步骤四:性能验证与关键指标
监控(上线前必须盯的计数器)
AG 复制通道
- SQLServer:Database Replica\Log Send Queue KB(主)
- SQLServer:Database Replica\Redo Queue KB(从)
- SQLServer:Availability Replica\Flow Control\*
事务与等待
- SQLServer:SQL Statistics\Batch Requests/sec
- sys.dm_os_wait_stats 中 HADR_SYNC_COMMIT、WRITELOG
SMB Direct
- \SMB Direct Connection(*)\Throughput
- Get-SmbMultichannelConnection(Rdma=True,通道数量、LinkSpeed)
IO
- sys.dm_io_virtual_file_stats(写延迟 ms/IO)
- 磁盘队列长度、NVMe 驱动延迟
网络
- 交换机端口 PFC 统计(优先级 3 的 pause 帧计数不应持续攀升)
我们那夜的对比数据(节选)
| 指标 | 优化前(无 RDMA/SMB Direct,播种走 TCP) | 优化后(RDMA + SMB Direct + QoS) |
|---|---|---|
| 首播种 1.2TB 耗时 | 3 小时 40 分 | 54 分钟 |
| 早盘峰值 P99 事务提交(ms) | 7.3 | 4.1 |
Log Send Queue KB(峰值) |
220,000+ | < 80,000 |
Redo Queue KB(峰值) |
150,000+ | ~ 50,000 |
| SQL 进程 CPU(播种期) | 60–70% | 25–35% |
| SMB 链路实际吞吐(单向) | ~ 6–8 Gbps | 21–23 Gbps(25G ×1 通道) |
交易高峰时,提交路径主要受 WRITELOG 与存储/日志盘影响。RDMA 并不会魔法般降低 WRITELOG,但通过释放网络与 CPU 抢占、加速播种/备份窗口、稳定副本队列,让系统在峰值更“稳态”,这就是我们观测到 P99 的改善来源。
步骤五:运营级别的细节优化(能救命的小技巧)
- 把 SMB/备份从业务网络“搬家”到 RDMA 平面:避免和客户端连接、AG TCP 竞争队列。
- 固定路由与 DNS 解析:SMB 指向 SOFS 的 A 记录只在 RDMA VLAN 可达;必要时用 New-SmbMultichannelConstraint 限制接口。
- Listener 只落在客户端网络:避免客户端“聪明”地往 RDMA 去连。
- NVMe 日志盘:保证 WRITELOG 的物理下限;把写入队列长度控制在稳定区间。
- 自动备份偏好:在 AG 上设置 AUTOMATED_BACKUP_PREFERENCE = SECONDARY,二副本承担备份,备份文件经 RDMA 出去,不打扰主库。
- 压测工具:HammerDB/TPC-E/TPC-C 自建脚本,尤其关注写密集混合负载下的 WRITELOG、HADR_SYNC_COMMIT 等等待类型。
现场踩坑与排障手记(真实发生)
坑 1:看起来 RDMA 了,实际上没有
症状:Get-SmbMultichannelConnection 里 Rdma=False,吞吐与 CPU 占用像普通 TCP。
排法:
- 查交换机与服务器 PFC/ETS/MTU 是否一致;
- Windows 端是否 约束到 RDMA 接口;
- Mellanox 驱动:确认 RoCE v2;
- Get-SmbClientNetworkInterface:RDMA Capable 必须为 True。
解决:发现交换机端某条上联口没开 PFC,补上后立刻 Rdma=True,吞吐爬升。
坑 2:PFC 暂停风暴
症状:RDMA 突然掉速、AG 复制延迟爬升、交换机队列不稳定。
原因:某台存储节点的微突发把整条优先级 3 拖住了。
解决:
- 把 ETS 的带宽分配更细(50–60% 给 SMB,留足其它队列);
- 在脊交换开启 ECN;
- 分散热点(SOFS 的 CSV 亲和、RDMA 通道数调整)。
坑 3:AG 创建后,端点连不上
症状:The server network address "TCP://..." cannot be reached...
排法:
- 防火墙是否放行 5022;
- 端点是否 STATE=STARTED;
- 端点身份验证是否使用域服务账号(避免证书错配);
- DNS 是否解析到业务网而不是 RDMA 网。
解决:清理旧证书配置,全部改为域服务账号,立刻恢复。
坑 4:Jumbo 配半截
症状:一端 9014,另一端 1500,吞吐上不去且有间歇性重传。
解决:端到端统一,链路所有口都验证 MTU,包括 LACP 成员。
坑 5:SMB 不走 RDMA 而走了管理口
症状:SOFS 的 A 记录在管理 VLAN 先被命中。
解决:给 SMB 访问显式指定 短名或 RDMA VLAN 的专用记录,并用 New-SmbMultichannelConstraint 钉死接口。
变更与回退策略(线上友好)
- 分阶段启用:先在 SOFS 与一台 SQL 之间开启 RDMA,验证无误后再扩到全体;
- DCB/ETS 变更窗口:先在链路下游(服务器端口)开,确认不误伤,再到上游(叶/脊);
- AG 只读副本先改造:把备份/只读流量先搬到 RDMA,主库不动;
- 回退路径:保留 TCP 访问共享的路径与策略,Remove-SmbMultichannelConstraint 即可回退。
运维清单(可复用)
- BIOS/固件/驱动统一到验证版本
- 交换机全链路 MTU 9216
- 服务器 NIC Jumbo 9014
- PFC on 3、ETS ≥ 50% 给 SMB
- Enable-NetAdapterRdma + Get-SmbMultichannelConnection 验证 Rdma=True
- SOFS 共享权限给 SQL 服务账号
- AG:主-从同步提交 + 自动切换;只读异步副本
- 备份偏好 Secondary;备份/播种走 \\sofs\...
- PerfMon 模板导入:HADR/SMB/IO/CPU/Network
- 回退与变更记录归档
FAQ:几个常见误解
“RDMA 会让 AG 复制更快吗?”
不会直接。AG 复制是 TCP 端点。但 RDMA 能把与 AG 并行的大流量 SMB从业务网络剥离出去,降低 CPU 与队列竞争,间接让系统更稳,从而改善高分位延迟。
“要不要 iWARP?”
RoCEv2 在数据中心更常见;iWARP 不需要 PFC,运维更简单。若你无法统一好 PFC/ETS,且有 iWARP NIC 资源,iWARP 也能是务实选择。
“Jumbo 一定有用吗?”
对大流量 SMB 有益,但必须端到端一致。否则弊大于利。
附:常用命令与脚本片段(收藏级)
PowerShell:一键校验 RDMA + SMB Direct
$server="sofs.corp.local"
Test-Connection $server -Count 2 | Out-Null
Write-Host "=== NIC & RDMA ==="
Get-NetAdapterRdma | ft Name,Enabled
Get-SmbClientNetworkInterface | ft InterfaceIndex,RdmaCapable,RssCapable,LinkSpeed
Write-Host "=== SMB MultiChannel ==="
($c = Get-SmbMultichannelConnection | where {$_.ServerName -eq $server})
$c | ft ServerName,ClientInterfaceIndex,Rdma,State,NumOfConnections
if($c -and ($c.Rdma -contains $true)) { "RDMA OK" } else { "RDMA NOT IN USE" }
T-SQL:关键 DMV 观察
-- HADR 队列
SELECT
ar.replica_server_name,
drs.database_id,
drs.log_send_queue_size, -- KB
drs.redo_queue_size, -- KB
drs.synchronization_state_desc,
drs.synchronization_health_desc
FROM sys.dm_hadr_database_replica_states drs
JOIN sys.availability_replicas ar ON drs.replica_id = ar.replica_id;
-- 写日志等待与 top waits
SELECT TOP 10 wait_type, wait_time_ms, 100.0 * wait_time_ms / SUM(wait_time_ms) OVER() AS pct
FROM sys.dm_os_wait_stats
ORDER BY wait_time_ms DESC;
早上 8:59,开盘前一分钟,监控墙上的 P99 线条像被人按住了头。交易开始后,AG 的 Log Send Queue 有起伏,但很快被压平;WRITELOG 的尖峰低了,SMB 通道吞吐稳定在 20Gbps 出头,SQL 进程 CPU 也没再飙。
走出机房时,外面开始热闹起来。那晚我们没动业务 SQL 一行代码,只是把流量与通道分好了层级,让该走 RDMA 的走 RDMA,让 AG 的 TCP 少受打扰。
做系统,很多时候不是“更快”,而是“更稳更可预期”。如果你也在香港的机房里和延迟较劲,希望这份记录能帮你把那条线按下去。
TL;DR(复刻步骤清单)
- 硬件就位:RDMA NIC(RoCEv2)、交换机开 DCB(PFC/ETS)、NVMe 日志盘;
- Windows:Enable-NetAdapterRdma、巨帧、New-NetQosPolicy(优先级 3)、New-NetQosTrafficClass、Enable/Disable-NetQosFlowControl;
- SOFS:S2D + 共享(备份/播种),权限给 SQL 服务账号;
- SMB Direct 验证:Get-SmbMultichannelConnection Rdma=True;
- AG:主/同步副本自动切换,异步只读副本;Listener 在客户端网;
- 备份/播种走 RDMA,业务走客户端网,路径隔离;
- 压测与监控:HADR 队列、SMB 吞吐、WRITELOG 等;
- 处理坑:PFC 风暴、Jumbo 不一致、端点/防火墙、DNS 接口绑错。