Skip to content

SQL Server 复制:快照复制、事务复制、合并复制

你的应用需要把数据分发到多个服务器:

  • 报表服务器:只读报表,不想影响主库
  • 远程分支机构:需要本地数据访问
  • 实时同步:多个服务器需要相同数据

数据库复制(Replication)是解决这些场景的利器。

这篇文章,带你全面理解 SQL Server 复制技术。


复制概述

什么是数据库复制?

复制 = 将数据从一个数据库「发布」到多个订阅数据库的技术。

┌─────────────────────────────────────────────────────────────┐
│                  SQL Server 复制架构                         │
│                                                              │
│  ┌──────────────┐              ┌──────────────┐              │
│  │   发布服务器   │    复制     │   分发服务器   │              │
│  │  (Publisher) │ ──────────► │  (Distributor)│              │
│  │              │              │              │              │
│  │  原始数据     │              │  存储复制数据  │              │
│  │              │              │  和历史记录    │              │
│  └──────────────┘              └──────────────┘              │
│                                        │                      │
│                                        │ 拉/推订阅            │
│                    ┌───────────────────┼───────────────────┐  │
│                    ▼                   ▼                   ▼  │
│  ┌──────────────┐ ┌──────────────┐ ┌──────────────┐      │
│  │   订阅服务器   │ │   订阅服务器   │ │   订阅服务器   │      │
│  │ (Subscriber) │ │ (Subscriber) │ │ (Subscriber) │      │
│  └──────────────┘ └──────────────┘ └──────────────┘      │
│                                                              │
│  ┌──────────────────────────────────────────────────────┐   │
│  │                    复制代理                           │   │
│  │  ┌──────────┐ ┌──────────┐ ┌──────────┐            │   │
│  │  │ 快照代理  │ │ 日志读取 │ │ 分发代理  │            │   │
│  │  │ Snapshot  │ │ Log Reader│ │ Distrib  │            │   │
│  │  └──────────┘ └──────────┘ └──────────┘            │   │
│  └──────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────┘

复制类型

类型数据同步延迟离线支持适用场景
快照复制全量定时初始化、低频更新
事务复制增量实时高实时性需求
合并复制双向同步多站点、离线工作

快照复制(Snapshot Replication)

工作原理

┌─────────────────────────────────────────────────────────────┐
│                  快照复制流程                                 │
│                                                              │
│  1. 快照生成                                               │
│     读取发布表数据                                          │
│     生成 BCP 文件和架构脚本                                 │
│     存储在快照文件夹                                        │
│                                                              │
│  2. 快照分发                                               │
│     分发代理读取快照文件                                    │
│     推送到订阅服务器(推送订阅)                             │
│     或订阅服务器拉取(拉取订阅)                            │
│                                                              │
│  3. 数据应用                                               │
│     订阅数据库应用快照                                      │
│     替换现有数据                                            │
└─────────────────────────────────────────────────────────────┘

配置快照复制

1. 配置分发服务器

sql
-- 配置分发服务器(如果尚未配置)
EXEC sp_adddistributor 
    @distributor = 'DistributorServer',
    @security_mode = 1;

EXEC sp_adddistributiondb 
    @database = 'distribution',
    @max_distretention = 72,
    @min_distretention = 60;

EXEC sp_adddistpublisher 
    @publisher = 'PublisherServer',
    @distribution_db = 'distribution',
    @security_mode = 1;

2. 创建发布

sql
-- 创建快照发布
USE [SalesDB];
EXEC sp_addpublication
    @publication = 'ProductsSnapshot',
    @status = 'active',
    @allow_push = N'true',
    @allow_pull = N'true',
    @description = N'产品表快照发布';

-- 添加项目
EXEC sp_addarticle
    @publication = 'ProductsSnapshot',
    @article = 'Products',
    @source_owner = 'dbo',
    @source_object = 'Products',
    @type = N'logbased',
    @ins_cmd = N'CALL sp_MSins_Products',
    @upd_cmd = N'CALL sp_MSupd_Products',
    @del_cmd = N'CALL sp_MSdel_Products';

-- 配置快照
EXEC sp_addpublication_snapshot
    @publication = 'ProductsSnapshot',
    @frequency_type = 1,  -- 在发布时
    @frequency_interval = 1;

3. 创建订阅

sql
-- 创建推送订阅
USE [SalesDB];
EXEC sp_addsubscription
    @publication = N'ProductsSnapshot',
    @subscriber = N'SubscriberServer',
    @destination_db = N'SalesDB_Replica',
    @subscription_type = N'push',
    @sync_type = N'automatic',
    @article = N'all',
    @update_mode = N'snapshot';

-- 创建拉取订阅
EXEC sp_addpullsubscription
    @publisher = N'PublisherServer',
    @publication = N'ProductsSnapshot',
    @publisher_db = N'SalesDB',
    @subscription_type = N'pull';

EXEC sp_addpullsubscription_agent
    @publisher = N'PublisherServer',
    @publisher_db = N'SalesDB',
    @publication = N'ProductsSnapshot',
    @distributor = N'DistributorServer',
    @run_continuous = N'false',
    @schedule_type = N'snapshot';

触发快照

sql
-- 手动触发快照
EXEC sp_startpublication_snapshot
    @publication = 'ProductsSnapshot';

-- 查看快照状态
SELECT 
    publication,
    publisher_db,
    publication_type,
    snapshot_location,
    incremental_timestamp
FROM MSsnapshot_agents;

-- 查看分发历史
EXEC sp_replmonitorsubscriptionpendingcmds
    @publisher = 'PublisherServer',
    @publisher_db = 'SalesDB',
    @publication = 'ProductsSnapshot',
    @subscriber = 'SubscriberServer',
    @subscriber_db = 'SalesDB_Replica';

事务复制(Transactional Replication)

工作原理

┌─────────────────────────────────────────────────────────────┐
│                  事务复制流程                                 │
│                                                              │
│  1. 日志读取(Log Reader Agent)                            │
│     监控发布数据库的事务日志                                  │
│     读取标记为复制的命令                                     │
│     写入分发数据库                                           │
│                                                              │
│  2. 分发(Distribution Agent)                               │
│     从分发数据库读取复制命令                                 │
│     发送到订阅服务器                                         │
│                                                              │
│  3. 应用(Distribution Agent)                               │
│     在订阅服务器上应用命令                                   │
│     保持事务一致性                                          │
│                                                              │
│  特点:低延迟、实时同步、单向传输                            │
└─────────────────────────────────────────────────────────────┘

配置事务复制

1. 创建发布

sql
-- 创建事务发布
USE [SalesDB];
EXEC sp_addpublication
    @publication = 'OrdersTransactional',
    @status = 'active',
    @allow_push = N'true',
    @allow_pull = N'false',
    @replicate_ddl = 1,
    @snapshot_in_defaultfolder = 'false',
    @alt_snapshot_folder = '\\SnapshotServer\Snapshots',
    @compression = 1;

-- 添加项目(带列筛选)
EXEC sp_addarticle
    @publication = 'OrdersTransactional',
    @article = 'Orders',
    @source_owner = 'dbo',
    @source_object = 'Orders',
    @type = N'logbased',
    @schema_option = 0x0000000008000331,
    @destination_table = 'Orders',
    @ins_cmd = N'CALL sp_MSins_Orders',
    @upd_cmd = N'SCALL sp_MSupd_Orders',
    @del_cmd = N'SCALL sp_MSdel_Orders';

-- 添加行筛选器
EXEC sp_addarticlefilter
    @publication = 'OrdersTransactional',
    @article = 'Orders',
    @filter_name = N'Filter_Orders_Active',
    @filter_clause = N'[status] IN (1, 2, 3)';

2. 创建订阅

sql
-- 创建订阅
USE [SalesDB];
EXEC sp_addsubscription
    @publication = 'OrdersTransactional',
    @subscriber = 'SubscriberServer',
    @destination_db = 'OrdersReplica',
    @subscription_type = N'push',
    @sync_type = N'automatic',
    @article = N'all';

-- 配置读取优先级
EXEC sp_helpsubscription
    @publication = 'OrdersTransactional';

监控事务复制

sql
-- 查看复制状态
SELECT 
    publisher,
    publisher_db,
    publication,
    distributor,
    distributor_db,
    publication_type
FROM MSpublications;

-- 查看订阅状态
SELECT 
    publisher,
    publisher_db,
    publication,
    subscriber,
    subscriber_db,
    subscription_type,
    sync_type,
    status
FROM MSsubscriptions;

-- 查看复制延迟
SELECT 
    agent_id,
    runstatus,
    delivered_transactions,
    delivered_commands,
    average_commands,
    delivery_rate
FROM MSlogreader_agents;

-- 查看分发队列
EXEC sp_replmonitorshowpendingcmds
    @publication = 'OrdersTransactional',
    @publisher = 'PublisherServer',
    @publisher_db = 'SalesDB';

合并复制(Merge Replication)

工作原理

┌─────────────────────────────────────────────────────────────┐
│                  合并复制流程                                 │
│                                                              │
│  1. 初始化                                                 │
│     快照代理创建初始数据                                    │
│     应用到所有订阅服务器                                    │
│     分配唯一标识符给每个订阅服务器                          │
│                                                              │
│  2. 离线工作                                               │
│     每个站点独立修改数据                                    │
│     记录在本地冲突表中                                      │
│                                                              │
│  3. 同步合并                                               │
│     Merge Agent 连接所有订阅服务器                          │
│     按优先级合并更改                                        │
│     检测并解决冲突                                          │
│                                                              │
│  特点:支持双向同步、离线工作、冲突解决                      │
└─────────────────────────────────────────────────────────────┘

配置合并复制

1. 创建发布

sql
-- 创建合并发布
USE [SalesDB];
EXEC sp_addmergepublication
    @publication = 'SalesDataMerge',
    @status = 'active',
    @snapshot_in_defaultfolder = 'false',
    @alt_snapshot_folder = '\\SnapshotServer\MergeSnapshots',
    @description = N'销售数据合并发布',
    @allow_subscriber_initiated_snapshot = N'true',
    @sync_mode = N'native',
    @retention = 14,
    @keep_partition_changes = N'true';

-- 添加项目和筛选
EXEC sp_addarticle
    @publication = 'SalesDataMerge',
    @article = 'Customers',
    @source_owner = 'dbo',
    @source_object = 'Customers',
    @type = N'table',
    @schema_option = 0x000000000CF5D2,
    @destination_table = 'Customers',
    @priority = 50;

-- 添加参数化筛选器
EXEC sp_addmergefilter
    @publication = 'SalesDataMerge',
    @article = 'Orders',
    @filtername = N'Filter_Orders_ByRegion',
    @filter_clause = N'[region_id] = HOST_NAME()',
    @join_articlename = N'Customers',
    @join_filterclause = N'Customers.customer_id = Orders.customer_id',
    @join_unique_key = 1;

2. 创建订阅

sql
-- 创建订阅
USE [SalesDB];
EXEC sp_addmergesubscription
    @publication = N'SalesDataMerge',
    @subscriber = N'SubscriberServer',
    @subscriber_db = N'CustomerDB',
    @subscription_type = N'push',
    @sync_type = N'Automatic',
    @hostname = N'RegionEast';  -- 匹配筛选器

-- 配置优先级
EXEC sp_changemergesubscription
    @publication = N'SalesDataMerge',
    @subscriber = N'SubscriberServer',
    @subscriber_db = N'CustomerDB',
    @property = N'priority',
    @value = 75;

冲突解决

sql
-- 查看冲突表
SELECT * FROM MSmerge_conflicts_info;

-- 查看冲突数据
SELECT * FROM Orders_MSTABConflicts;

-- 解决冲突(手动)
-- 保留特定版本的更改
UPDATE Orders_MSTAB
SET order_status = 'resolved'
WHERE order_id = 1001;

-- 配置冲突解决策略
-- 优先级:谁高用谁
-- 首次发布优先:先处理的服务器
-- 最新的更改优先:最后修改的
EXEC sp_changemergepublication
    @publication = 'SalesDataMerge',
    @property = 'conflict_retention',
    @value = 7;  -- 保留冲突天数

对比与选择

三种复制的对比

特性快照复制事务复制合并复制
数据同步全量增量双向增量
同步延迟高(分钟-小时)低(秒-分钟)中(分钟)
离线工作
双向同步
冲突解决
订阅服务器修改
数据量小-中
网络要求

选择指南

场景推荐复制类型
报表数据库快照复制
高实时性分发事务复制
读写分离事务复制
多分支机构合并复制
离线应用合并复制
跨地域同步合并复制
DR 复制事务复制(异步)

常见问题

问题 1:复制延迟高

sql
-- 检查日志读取延迟
SELECT 
    publisher,
    publisher_db,
    publication,
    runstatus,
    time,
    delivered_transactions,
    delivery_rate
FROM MSlogreader_agents;

-- 检查分发延迟
SELECT 
    publisher,
    publisher_db,
    publication,
    subscriber,
    subscriber_db,
    runstatus,
    time,
    delivered_transactions,
    delivery_rate
FROM MSdistribution_agents;

-- 可能原因:
-- 1. 网络延迟
-- 2. 分发服务器性能不足
-- 3. 订阅服务器性能不足
-- 4. 大事务导致复制滞后

问题 2:复制冲突

sql
-- 检查合并复制冲突
SELECT 
    origin_datasource,
    conflict_table,
    conflict_type,
    conflict_text,
    CRYPTimestamp
FROM MSmerge_conflicts_info;

-- 解决冲突:保留发布服务器版本
DELETE FROM Orders_MSTABConflicts
WHERE origin_datasource = 'SubscriberServer';

-- 配置解决策略
EXEC sp_changemergearticle
    @publication = 'SalesDataMerge',
    @article = 'Orders',
    @property = 'conflict_resolution',
    @value = 'publisher wins';

问题 3:重建订阅

sql
-- 移除订阅
EXEC sp_dropsubscription
    @publication = 'OrdersTransactional',
    @subscriber = 'SubscriberServer',
    @destination_db = 'OrdersReplica',
    @article = N'all';

-- 重新初始化订阅
EXEC sp_refreshsubscriptions
    @publication = 'OrdersTransactional';

-- 或使用快照重新初始化
EXEC sp_reinitalize_subscription
    @subscriber = 'SubscriberServer',
    @subscriber_db = 'OrdersReplica',
    @invalidate_snapshot = 0;

面试追问方向

  • SQL Server 有哪几种复制类型?各自的特点是什么?
  • 事务复制的工作原理是什么?
  • 快照复制和事务复制的核心区别是什么?
  • 合并复制有什么特点?适用于什么场景?
  • 什么是复制冲突?如何解决?
  • 如何监控复制延迟?

下一步

理解了复制技术,我们来看 SQL Server 备份策略与恢复模式,学习数据保护的基础。

基于 VitePress 构建