SQL Server事务复制实现跨地市主从数据库准实时同步实战
2026/9/16 3:37:03 网站建设 项目流程

上个月刚收了一个跨地市的数据库同步项目,业务方要求把总部生产库里的十几张核心表,准实时地同步到外地的报表库。两台机器都是SQL Server 2014,中间走的是普通互联网,没有专线,也没有内网打通。我最终落地用的是SQL Server自带的复制(Replication)功能,也就是题主说的“主从数据库订阅和发布”。这套方案做完之后同步延迟稳定在10秒以内,跑了一个月基本没出过需要人工干预的问题。今天把这套“总部发布—异地订阅”的完整配置过程做一个超详细复盘,覆盖方案选型、环境规划、发布端配置、订阅端配置,以及我在调试过程中踩过的坑和最终的排查思路。

这篇内容适合正在做SQL Server 2014数据库分发、或者想把生产库的部分表跨互联网同步到异地的朋友参考。如果你只是想本地局域网内做读写分离,这里的大部分经验同样适用,但公网环境下的网络配置和初始化思路,可能会给你省下不少试错时间。

1. 方案选型:为什么在2014时代还用复制,以及选哪种复制

1.1 先搞清楚“主从复制”在SQL Server里到底是什么

很多人一说主从数据库,第一反应就是MySQL里的Master-Slave、Binlog同步那一套。SQL Server的“订阅和发布”机制其实不同于MySQL主从,它的本质是“分发”模型:一台发布服务器(Publisher)维护一份用于复制的数据快照和事务日志读取机制,把数据变更记录传递到分发服务器(Distributor),再由分发代理推送到一台或多台订阅服务器(Subscriber)。

从业务角度看,这就是标准的“一写多读”场景:总部库持续写入,外地库只做报表查询。你不需要在订阅端做任何DML操作,数据是单向流动的,从总部到外地。

SQL Server 2014虽然已经是老版本,但复制功能在这个版本里非常成熟稳定。相比AlwaysOn可用性组或者日志传送,复制有一个非常大的优势:它可以做到表级粒度。也就是你不需要把整个数据库同步过去,只需要勾选特定表、特定存储过程,甚至按行筛选和按列筛选。对于只需要把“基础数据表”同步到异地报表库的场景,复制是性价比最高的选择。

1.2 快照复制、事务复制、合并复制,选哪个

SQL Server复制一共有三种类型:快照复制(Snapshot)、事务复制(Transactional)、合并复制(Merge)。我直接说结论,事务复制是最主流的主从订阅方案,快照复制和合并复制在这个场景下都有明显短板。

  • 快照复制:把所有发布的数据按固定时间点生成一张快照,然后整体复制到订阅端。特点是周期性、全量。它只适合数据量极小、不要求实时性的场景,比如每日刷新的价格表。对于频繁变化的业务表,它会产生较大的快照文件,而且两次快照之间的变更无法捕获。
  • 事务复制:从发布端读取事务日志(Log Reader),把增量事务逐条(或者按批)应用到订阅端。延迟通常在秒级。它要求每张发布表必须有一个主键,复制机制本身依赖主键来定位行。
  • 合并复制:允许发布端和订阅端双向修改数据,然后通过冲突解决机制合并。这种模式配置复杂,而且只适合偶尔断开连接的移动办公场景,绝大多数“主从同步”项目都不需要。

所以,只要你的业务是“总部写入、外地只读”,事务复制就是首选。题主说的“主从数据库订阅和发布”,从技术本质上就是事务复制,发布端是主库,订阅端是从库。

1.3 推送订阅还是拉取订阅,公网场景选哪个

复制创建订阅时,可以根据分发代理的运行位置分为推送订阅(Push,代理运行在分发服务器上)和拉取订阅(Pull,代理运行在订阅服务器上)。

在局域网内部,选推送订阅最方便,因为所有任务都集中在总部管理。但在“异地订阅—互联网”这个场景里,我强烈建议选择拉取订阅

原因有两点:

第一,公网环境下网络不稳定,订阅端断线重连是常态。拉取订阅由订阅端发起连接,相当于“由外向内”主动拉数据,一旦网络恢复,代理会自动重试,不需要总部那边做任何干预。

第二,推送订阅要求总部服务器能主动访问到外地的1433端口,这意味着外地那台机器必须暴露公网监听地址,网络边界上通常不允许这么做。而拉取订阅,是外地机器主动连接总部数据库,网络策略只需要开放总部的数据库端口给外地方向即可,安全性和可控性都好很多。

我最终用的就是事务复制 + 拉取订阅。下面所有配置都以这个组合为准。

2. 环境规划和网络准备:异地互联网最容易翻车的环节

2.1 版本、账号和权限如何对齐

动手配置前,务必先确认三件事:SQL Server版本、Agent服务状态、同步账号权限。

我在这个项目里,发布端和订阅端都是SQL Server 2014 Standard Edition,这有一个好处:复制是包含在Standard版里的功能,不需要企业版许可。注意,如果两端版本不一致,比如发布端是2014、订阅端是2016,原则上没问题,但我建议尽量同大版本。跨大版本的复制虽然支持,但快照格式、代理升级逻辑有时候会闹脾气,不是必须就别冒险。

发布端的SQL Server Agent必须设置为“自动启动”,并且用本地系统账户或域账户运行。复制里的快照代理、日志读取器代理都是挂在Agent下面的作业,Agent没起来,后面所有复制任务都会卡住。

账号权限方面,我建议为复制单独建一个登录名,比如repl_user,授予sysadmin服务器角色,或者至少授予db_owner(发布数据库)、分发数据库的db_owner,以及分发服务器上的replmonitor权限。很多人偷懒直接用sa,在生产环境里风险很大,后面替换账号也很麻烦。而且,账号的密码务必设置为“永不过期”。

2.2 公网怎么访问数据库,端口和防火墙处理

异地订阅的“异地”两个字,决定了这一步绕不开。总部的SQL Server实例如果是默认实例,监听的是1433端口。你需要在总部的防火墙上放行这个端口,允许外地的订阅服务器IP访问。千万别图省事对全网开放,把防火墙入站规则限制为只有订阅端公网IP能访问

如果总部数据库不是默认实例,而是命名实例,那监听端口是动态的(默认49152之后的随机端口),你需要先在SQL Server配置管理器里给这个实例固定一个端口,比如14333,然后再在防火墙里放行这个端口。否则你会遇到一个特别经典的坑:明明telnet通了一个端口,过一会儿又连不上了,那就是动态端口在跳。

还有一个很容易忽略的地方:SQL Server Browser服务。如果你用命名实例,订阅端连接的时候需要解析实例名,SQL Server Browser默认监听UDP 1434。但在公网环境,我的建议是别开放UDP,直接用IP,端口的形式写订阅服务器地址,比如192.168.1.10,14333,这样就不依赖Browser服务,少一个网络依赖就少一分故障可能。

配置完成后,先在订阅端那台机器上用SQL Server Management Studio(SSMS)尝试连接发布端实例,能连上再做后面的操作。这一步做不通,后面所有的“发布”“订阅”都是空中楼阁。

2.3 快照文件夹的共享,必须在配置前想清楚

这是所有SQL Server复制项目实施时最容易翻车的一步。创建发布时,向导会让你指定一个“快照文件夹”路径,默认是发布服务器上的本地目录,比如C:\Program Files\Microsoft SQL Server\MSSQL12.MSSQLSERVER\MSSQL\ReplData

问题在于,订阅端初始化数据时,分发代理要从这个路径读取快照文件。如果你用的是局域网共享,通常把这个目录设为共享目录,并且授予订阅端机器相应的读权限就行。但是,公网环境下SMB共享非常不可靠,往往伴随着防火墙对139/445端口的封锁、域认证失败、共享权限混乱等一堆问题。

在这个项目里,我并没有让订阅端直接通过公网SMB去读总部的快照文件夹,而是采用了后面会讲到的“备份初始化 + 仅支持复制”方案,彻底绕开了跨公网传输快照文件这个大坑。所以你先把“快照文件夹”这个概念记住,后面初始化那一步我会详细说怎么处理。

3. 发布端配置全流程:从配置分发到生成快照

3.1 配置分发服务器,发布库和分发库放一起行不行

对于节点数量不多、数据变更量适中的项目,我建议直接把分发服务器和发布服务器放在同一台机器上,也就是“本地分发”(Local Distributor)。好处是少了一台机器的部署和网络依赖,维护起来简单。如果做高并发大事务,分发库(distribution)会占用较多磁盘IO和日志增长空间,到时候再拆出去也行。

操作路径:SSMS中右键SQL Server实例 ->复制->配置分发

向导会让你选择分发数据库文件位置。默认放在数据目录下。这里有个实践细节:把分发数据库的日志文件放到一个和主库日志文件物理分开的磁盘上,避免日志写竞争。如果你只有一块盘,那就没办法了,但至少要知道这个优化点。

配置完成后,实例下会多出一个“复制”节点,同时SQL Server Agent里会多出几个复制相关作业:

  • [Repl-Distributor-MSSQLSERVER]:复制代理历史记录清理
  • [Repl-HistoryMaintenance-MSSQLSERVER]:复制历史记录维护

这两个是系统级作业,留着别动。

3.2 创建发布项目:事务发布的具体设置

右键本地发布->新建发布,选择你要发布的数据库。这里建议单独建一个只含同步对象的发布,不要把所有表一股脑选上,后面加表也方便。

点击“发布对象”时,注意这个界面按对象类型分了几个标签:表、存储过程、视图等。我们这次只勾选需要同步的业务表。选表后,系统会对每张表做检查,其中最关键的一条:表必须有主键

如果你的表没有主键,在点击下一步时你会看到告警图标,提示“无法将表发布,因为未包含主键列”。解决办法只有两个:要么业务表补主键,要么不发布这张表。事务复制就是这么硬性要求,没有主键就没有行定位能力,增量事务无法应用到订阅端。我遇到过业务部门死活不让人动表结构的情况,后来专门给表加了一个自增ID主键作为代理键,才算过审。

再往后有个“项目属性”按钮,里面可以设置NOT FOR REPLICATION选项,这个选项表示“由复制分发过来的操作不触发某些约束或触发器”。比如你表上有插入触发器,同步过来的数据本来就不该再触发一次业务触发器,这时候就应该把触发器属性里的“用于复制”勾选上。这块确实容易忽略,但大多数场景下向导默认值就够了。

发布类型选“事务发布”,然后设置快照代理的执行时间。默认选择“立即创建快照”,这个可以先选着,但真正初始化订阅时不用公网SMB传输,快照只是生成在本地而已。

3.3 发布访问列表和安全设置,这步最容易报错

创建发布向导走到最后,会让你管理“发布访问列表”(Publication Access List,简称PAL)。这个列表决定了哪些登录名有权限订阅这个发布。必须把你订阅端用来连接发布端的账号加进去,否则订阅端创建订阅时会直接报“对发布服务器的访问被拒绝”。

在这个项目里,我把repl_user加了进去,并勾选了“分发代理”和“日志读取器代理”的权限。这一步很多人漏掉,结果订阅端配置时反复报错,还以为是网络不通。如果你用的是sa,那默认就在PAL里,这也是为什么网上教程喜欢让用sa的原因,但实际生产项目里还是建议用专用账号,安全上踏实很多。

完成创建后,如果你的发布数据库启用了变更数据捕获(CDC)或者透明数据加密(TDE),都需要额外的兼容措施。SQL Server 2014时代TDE和复制共存时,快照文件是明文还是密文由选项控制,这个项目没遇到,这里先不展开。

快照生成后,你可以在C:\Program Files\Microsoft SQL Server\MSSQL12.MSSQLSERVER\MSSQL\ReplData下看到类似MSSQLSERVER_数据库名_发布名_时间戳.snapshot的文件。这只能说明快照生成了,还不能说明订阅端能用,下一步才是重头戏。

4. 订阅端配置全流程:建拉取订阅和可靠的初始化方式

4.1 创建订阅,为什么代理必须选“在订阅服务器端运行”

在订阅端,依次展开复制->本地订阅,右键新建订阅

第一步选发布服务器,填写的总部服务器地址。因为是公网,建议用IP加逗号加端口的方式,避免实例名解析问题。连接成功后,会出现你刚刚创建的发布,选中它。

接下来最关键的一步:选择“分发代理位置”。这里有两个选项:

  • 在分发服务器上运行(推送订阅)
  • 在订阅服务器上运行(拉取订阅)

选择“在订阅服务器上运行”。正如前面说的,拉取订阅让外地这台机器主动去总部拉数据,网络策略和重试机制都更符合公网场景。

之后会让你选择同步数据库,也就是订阅数据库。我建议先手工在订阅端创建一个空库,比如ReportDB,然后在向导里选这个库。如果你在向导里就地新建数据库,后续的索引、约束布置都会在快照应用时创建,问题不大,但手工创建库时可以预先设置文件大小和自动增长策略,更可控。

向导会让你填写订阅服务器到发布服务器(以及分发服务器)的连接凭据。这里填repl_user的账号密码。如果要走Windows身份验证,你得确保两端机器之间能互信,公网环境通常做不到,所以还是用SQL Server身份验证。

4.2 初始化订阅的两种方式,我不推荐跨公网传快照

创建订阅的向导里,“初始化订阅”默认是勾选的,同时会让你选“初始化时间”:立即或“仅支持复制”。

这里需要仔细说清楚。“立即初始化”意味着分发代理会从快照文件夹读取快照文件并应用到订阅端,如果是局域网共享目录,这个流程没问题。但我们的场景是公网,订阅端想去访问总部的SMB共享文件夹,基本行不通,公网防火墙默认不会放行139/445端口,即便放行也慢得让人崩溃。

所以我直接选择了“不初始化”的方式,分两步走:

第一步,用SQL Server的BACKUP DATABASE把发布数据库备份出来,传到订阅端,然后用RESTORE DATABASE恢复到这个空的ReportDB里。注意恢复时必须用WITH NORECOVERY吗?不需要,正常恢复就行,但如果你用了差异备份或者日志备份,才需要考虑恢复链。我的做法是:总部凌晨做一次完整备份,当天晚上在订阅端恢复,第二天上午启动订阅。业务数据延迟一天启动,对只读报表影响不大。

第二步,在订阅向导的“初始化订阅”部分,选择“仅支持复制”,时间随便选。这样复制代理就不会尝试去读快照文件了,而是直接认为订阅端已经有了和发布端一致的结构和数据。之后日志读取器从某个LSN点开始把后续变更补过来。

就这么一个小改动,避开了公网跨SMB读快照这整个大坑。这也是我这个项目能快速落地的最关键操作之一。

注意:使用“仅支持复制”的前提是,订阅端数据库的数据必须和发布端在某个时间点上完全一致,包括表结构、主键、索引、约束。如果两边数据不一致,复制启动后会出现主键冲突或者找不到行的错误。

4.3 验证同步结果:几分钟看一次分发代理历史记录

订阅创建完成后,在订阅端打开SQL Server Agent,找到名为类似“[MSrepl-订阅库名-发布库名-发布名-订阅服务器名称-发布服务器名称]”的作业。右键作业开始步骤,前台跑一次。

跑完之后,去“复制监视器”或者订阅端右键该订阅 ->查看同步状态,你会看到历史记录。重点看两个指标:

  • 投递的事务数
  • 投递的命令数

如果这两个数值持续增长,说明日志读取器和分发代理都在正常工作。

为了验证数据,我在仪表库之外还额外同步了一张只有几十行的小配置表。启动订阅之后,我先看这张表是否和历史数据对得上,然后在总部库里随意修改一行,等10秒左右,再查订阅端,如果变化同步过来了,整条链路就通了。

SQL Server 2014的复制延迟通常在几秒到十几秒之间。如果超过一分钟还没见到变更,就说明有阻塞或者网络异常,需要排查。

5. 常见问题排查实录与避坑清单

5.1 快照文件无法访问,十有八九是权限和UNC路径

就算你不跨公网读快照,本地生成快照时也可能遇到“无法访问文件夹”的报错。常见原因:

  • 快照文件夹是发布服务器本地的C$管理共享,而SQL Server Agent服务账户没有该共享的权限。解决办法:把快照文件夹设成一个普通目录,比如D:\ReplData,并共享给Everyone(至少给读权限),或者明确授权给Agent服务账户。
  • 订阅端代理无法访问发布端的共享目录。如果你一定要用“立即初始化”走公网,那就别选SMB共享,而是把快照文件手工拷贝到订阅端本地目录,再把订阅端快照文件夹路径改为本地路径。这个操作在订阅属性的“快照位置”里可以改。

但最省事的还是前面那套:备份恢复 + 仅支持复制。

5.2 代理作业失败,先看这两个服务有没有起来

复制代理失败,很多人第一反应去看网络、看权限,其实我更建议先检查服务:

  • 发布服务器和订阅服务器上的SQL Server Agent服务是否处于“正在运行”状态。Agent服务停了,日志读取器和分发代理都不会执行。
  • 如果是拉取订阅,注意看订阅端Agent的作业里,步骤是否明确指向“分发代理”。

有一种特别隐蔽的情况:Agent作业上一次因为网络中断失败后,一直卡在“正在重试”状态,导致后续作业一直排队。这时候的做法是右键作业 ->停止作业,然后重新启动步骤。SQL Server复制的代理重试机制是有的,但公网断连几分钟后,作业状态经常需要人工干预。

5.3 数据同步延迟越来越高,网络和包大小怎么调

如果一开始同步正常,跑着跑着延迟越来越大,我逐个排查过这几个点:

  • 发布数据库的事务日志是否积压。日志读取器是通过读取日志来捕获变更的,如果事务日志文件不动,可能是日志读取器代理卡住了,或者日志备份任务长时间没执行导致日志文件超大。
  • 网络带宽确实不够。我的解决方式是在分发代理配置文件的-MaxDeliveredTransactions参数里,限制每次投递的事务数,避免大事务把窄带宽瞬间占满。一个经验值:公网带宽在10Mbps以下时,把每批投递事务数控制在500左右。
  • 订阅端表上的索引碎片化严重,导致应用事务变慢。可以定期重建订阅端报表库的索引,这个不影响复制主链路。

另一个值得注意的点:发布服务器的日志读取器代理使用的是“读取事务日志”模式,它每执行一次,都会对发布库产生额外的IO开销。在高峰期如果你发现总部库IO有明显上升,可以通过设置“分发代理历史记录保留期”等方式减少不必要的扫描,但本质上这是事务复制的固定成本,数据同步本来就不是免费午餐。

5.4 复制长期运行后账号失效,建议从一开始就用永不过期账号

项目上线后第三周,收到异地报表库报警,发现数据不更新了。我查了订阅状态,发现分发代理连着报“登录失败”。原因很简单:那个repl_user账号的密码在服务器上被组策略强制过期了。

这个问题说大不大,但排查起来特别干扰判断。我现在做复制项目,从第一天起就要求:

  • 复制专用登录名密码设置为“永不过期”。
  • 日志读取代理和分发代理的连接配置里,密码更新后要同步修改作业步骤里的连接字符串。
  • 建议定期人工复核复制状态,比如写一个简单的PowerShell脚本,定时读取订阅端同步延迟,超过阈值就发告警邮件。这比等业务方打电话来要主动得多。

5.5 一张速查表:复制状态怎么看

为了方便日常排查,我把最常用的几个查询整理成一个速查表:

检查项查询/方法判断标准
发布是否正常发布服务器 -> 复制 -> 本地发布,右键查看属性状态为“正常”
订阅是否正常订阅端 -> 复制 -> 本地订阅,双击订阅最后投递时间不断更新
日志读取器是否卡住发布端 -> 复制 -> 查看代理状态日志读取器最近历史无错误
待分发的命令数分发数据库中执行sp_browsereplcmds结果数量持续为0
同步延迟订阅端查看分发代理历史记录投递延迟小于设定阈值

复制出问题最怕的就是靠猜,把上面几项一查,基本都能定位。

最后再分享一点实际体会

这一套SQL Server 2014的主从数据库订阅和发布做下来,我最大的体会是:复制本身不复杂,复杂的是网络和初始化方式。只要版本统一、账号权限规划好、发布访问列表配置对、初始化采用“备份恢复 + 仅支持复制”,异地互联网场景下也可以做到稳定运行。SQL Server复制的灵活性远超想象,这个项目做完,后续如果有人提出“再增加一个节点”或者“只同步某几张表”,在原有发布上直接加订阅就好,几分钟的事。复制这个老功能,在可靠性和易用性上依然值得信赖。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询