一、数据复制概述
数据复制是将数据从一个数据库节点复制到其他节点的技术,能够提高数据可用性、实现读写分离、提供灾难恢复能力。数据复制是构建高可用数据库架构的关键技术。
二、复制策略对比
2.1 复制策略对比表
| 策略 | 描述 | 一致性 | 性能 | 可用性 | 适用场景 |
|---|---|---|---|---|---|
| 同步复制 | 等待所有副本确认 | 强一致性 | 低 | 低 | 金融交易 |
| 异步复制 | 不等待副本确认 | 最终一致性 | 高 | 高 | 一般业务 |
| 半同步复制 | 等待部分副本确认 | 中等一致性 | 中等 | 中等 | 平衡场景 |
| 多主复制 | 多个主节点互相同步 | 最终一致性 | 高 | 高 | 多地域部署 |
2.2 复制策略选择
flowchart TD
A[选择复制策略] --> B{一致性要求}
B -->|强一致性| C[同步复制]
B -->|最终一致性| D[异步复制]
B -->|平衡| E[半同步复制]
A --> F{部署方式}
F -->|多地域| G[多主复制]
F -->|单地域| H[主从复制]
C --> C1[金融、交易]
D --> D1[一般业务]
E --> E1[电商、社交]
G --> G1[全球部署]
三、同步复制
3.1 同步复制原理
同步复制等待所有副本确认后才返回成功:
sequenceDiagram
participant Client as 客户端
participant Master as 主节点
participant Slave1 as 从节点1
participant Slave2 as 从节点2
Client->>Master: 写入请求
Master->>Master: 写入数据
Master->>Slave1: 复制数据
Master->>Slave2: 复制数据
Slave1-->>Master: 确认
Slave2-->>Master: 确认
Master-->>Client: 返回成功
3.2 MySQL同步复制配置
-- 主节点配置
server-id = 1
log_bin = /var/log/mysql/mysql-bin.log
binlog_format = ROW
sync_binlog = 1
innodb_flush_log_at_trx_commit = 1
-- 从节点配置
server-id = 2
relay_log = /var/log/mysql/relay-bin.log
read_only = 1
-- 启用半同步复制(MySQL 5.6+)
plugin-load-add = rpl_semi_sync_master.so
plugin-load-add = rpl_semi_sync_slave.so
-- 主节点设置同步模式
SET GLOBAL rpl_semi_sync_master_enabled = 1;
SET GLOBAL rpl_semi_sync_master_timeout = 10000;
3.3 PostgreSQL同步复制配置
-- postgresql.conf 主节点配置
wal_level = replica
max_wal_senders = 10
wal_keep_size = 1GB
-- postgresql.conf 从节点配置
hot_standby = on
max_standby_streaming_delay = 30s
-- pg_hba.conf 允许复制连接
host replication repuser 192.168.1.0/24 md5
-- 设置同步复制
ALTER SYSTEM SET synchronous_standby_names = 'slave1, slave2';
SELECT pg_reload_conf();
-- 查看同步状态
SELECT usename, application_name, state, sync_state FROM pg_stat_replication;
四、异步复制
4.1 异步复制原理
异步复制不等待副本确认,立即返回成功:
sequenceDiagram
participant Client as 客户端
participant Master as 主节点
participant Slave as 从节点
Client->>Master: 写入请求
Master->>Master: 写入数据
Master-->>Client: 返回成功
Master->>Slave: 异步复制数据
Slave->>Slave: 应用数据
4.2 MySQL异步复制配置
-- 主节点配置
server-id = 1
log_bin = /var/log/mysql/mysql-bin.log
binlog_format = ROW
-- 创建复制用户
CREATE USER 'repl'@'%' IDENTIFIED BY 'password';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';
-- 查看主节点状态
SHOW MASTER STATUS;
-- 从节点配置
server-id = 2
relay_log = /var/log/mysql/relay-bin.log
read_only = 1
-- 配置复制
CHANGE MASTER TO
MASTER_HOST='master_host',
MASTER_USER='repl',
MASTER_PASSWORD='password',
MASTER_LOG_FILE='mysql-bin.000001',
MASTER_LOG_POS=107;
-- 启动复制
START SLAVE;
-- 查看复制状态
SHOW SLAVE STATUS\G
4.3 MongoDB异步复制
# 创建副本集配置
rs.initiate({
_id: "rs0",
members: [
{ _id: 0, host: "mongo1:27017" },
{ _id: 1, host: "mongo2:27017" },
{ _id: 2, host: "mongo3:27017" }
]
})
# 查看副本集状态
rs.status()
# 设置读写偏好
db.collection.find().readPref("secondaryPreferred")
# 强制读主节点
db.collection.find().readPref("primary")
五、半同步复制
5.1 半同步复制原理
半同步复制等待部分副本确认后返回成功:
sequenceDiagram
participant Client as 客户端
participant Master as 主节点
participant Slave1 as 从节点1
participant Slave2 as 从节点2
Client->>Master: 写入请求
Master->>Master: 写入数据
Master->>Slave1: 复制数据
Master->>Slave2: 复制数据
Slave1-->>Master: 确认(达到阈值)
Master-->>Client: 返回成功
Slave2-->>Master: 确认(异步)
5.2 MySQL半同步复制配置
-- 安装半同步插件
INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';
-- 主节点启用半同步
SET GLOBAL rpl_semi_sync_master_enabled = 1;
SET GLOBAL rpl_semi_sync_master_wait_for_slave_count = 1;
SET GLOBAL rpl_semi_sync_master_timeout = 10000;
-- 从节点启用半同步
SET GLOBAL rpl_semi_sync_slave_enabled = 1;
-- 重启从节点复制
STOP SLAVE IO_THREAD;
START SLAVE IO_THREAD;
-- 查看半同步状态
SHOW STATUS LIKE 'Rpl_semi_sync_master_status';
SHOW STATUS LIKE 'Rpl_semi_sync_slave_status';
5.3 半同步复制监控
public class SemiSyncReplicationMonitor
{
public async Task<ReplicationMetrics> GetMetrics()
{
var masterStatus = await _dbContext.QuerySingleAsync<MasterStatus>(
"SHOW STATUS LIKE 'Rpl_semi_sync_master_status'"
);
var slaveCount = await _dbContext.QuerySingleAsync<SlaveCount>(
"SHOW STATUS LIKE 'Rpl_semi_sync_master_wait_for_slave_count'"
);
var slaveStatus = await _dbContext.QueryAsync<SlaveStatus>(
"SHOW SLAVE STATUS"
);
return new ReplicationMetrics
{
IsSemiSyncEnabled = masterStatus.Value == "ON",
RequiredSlaveCount = int.Parse(slaveCount.Value),
ConnectedSlaves = slaveStatus.Count(),
HealthySlaves = slaveStatus.Count(s => s.IoRunning == "Yes" && s.SqlRunning == "Yes")
};
}
}
六、多主复制
6.1 多主复制原理
多主复制允许多个节点同时接受写入:
graph TD
A[主节点1] --> B[主节点2]
A --> C[主节点3]
B --> A
B --> C
C --> A
C --> B
D[客户端1] --> A
E[客户端2] --> B
F[客户端3] --> C
6.2 MySQL多主复制配置
-- 节点1配置
server-id = 1
log_bin = /var/log/mysql/mysql-bin.log
binlog_format = ROW
auto_increment_offset = 1
auto_increment_increment = 3
-- 节点2配置
server-id = 2
log_bin = /var/log/mysql/mysql-bin.log
binlog_format = ROW
auto_increment_offset = 2
auto_increment_increment = 3
-- 节点3配置
server-id = 3
log_bin = /var/log/mysql/mysql-bin.log
binlog_format = ROW
auto_increment_offset = 3
auto_increment_increment = 3
-- 配置互相复制
CHANGE MASTER TO
MASTER_HOST='node2',
MASTER_USER='repl',
MASTER_PASSWORD='password',
MASTER_LOG_FILE='mysql-bin.000001',
MASTER_LOG_POS=107;
CHANGE MASTER TO
MASTER_HOST='node3',
MASTER_USER='repl',
MASTER_PASSWORD='password',
MASTER_LOG_FILE='mysql-bin.000001',
MASTER_LOG_POS=107 FOR CHANNEL 'node3';
6.3 PostgreSQL多主复制(使用BDR)
# 安装BDR扩展
CREATE EXTENSION bdr;
# 初始化BDR节点
SELECT bdr.bdr_group_create(
local_node_name := 'node1',
node_external_dsn := 'host=node1 port=5432 dbname=app'
);
# 添加其他节点
SELECT bdr.bdr_group_join(
local_node_name := 'node2',
node_external_dsn := 'host=node2 port=5432 dbname=app',
join_using_dsn := 'host=node1 port=5432 dbname=app'
);
# 查看BDR状态
SELECT * FROM bdr.bdr_nodes;
6.4 多主复制冲突解决
public class MultiMasterConflictResolver
{
public async Task ResolveConflict(ConflictInfo conflict)
{
switch (conflict.Type)
{
case ConflictType.Insert:
await ResolveInsertConflict(conflict);
break;
case ConflictType.Update:
await ResolveUpdateConflict(conflict);
break;
case ConflictType.Delete:
await ResolveDeleteConflict(conflict);
break;
}
}
private async Task ResolveUpdateConflict(ConflictInfo conflict)
{
var node1Data = await GetNodeData(conflict.Node1Id, conflict.TableName, conflict.PrimaryKey);
var node2Data = await GetNodeData(conflict.Node2Id, conflict.TableName, conflict.PrimaryKey);
var latestData = node1Data.UpdatedAt > node2Data.UpdatedAt ? node1Data : node2Data;
await ApplyResolvedData(conflict.TableName, conflict.PrimaryKey, latestData);
await _auditLogger.LogConflictResolution(conflict, ConflictResolutionStrategy.LastWriteWins);
}
private async Task ResolveInsertConflict(ConflictInfo conflict)
{
// 使用UUID避免主键冲突
await AssignNewId(conflict.Node2Id, conflict.TableName, conflict.PrimaryKey);
}
}
public enum ConflictResolutionStrategy
{
LastWriteWins,
FirstWriteWins,
Manual,
Custom
}
七、复制拓扑结构
7.1 复制拓扑类型
| 拓扑 | 描述 | 优点 | 缺点 |
|---|---|---|---|
| 一主一从 | 一个主节点一个从节点 | 简单、可靠 | 扩展性有限 |
| 一主多从 | 一个主节点多个从节点 | 读扩展性好 | 主节点压力大 |
| 级联复制 | 从节点再复制到其他从节点 | 减轻主节点压力 | 延迟增加 |
| 环形复制 | 节点互相复制 | 高可用 | 复杂、冲突风险 |
7.2 级联复制拓扑
graph TD
A[主节点] --> B[从节点1]
A --> C[从节点2]
B --> D[从节点1.1]
B --> E[从节点1.2]
C --> F[从节点2.1]
G[客户端读] --> D
G --> E
G --> F
H[客户端写] --> A
八、复制监控与管理
8.1 复制监控指标
public class ReplicationMetrics
{
public string MasterHost { get; set; }
public int ConnectedSlaves { get; set; }
public int HealthySlaves { get; set; }
public long ReplicationLagSeconds { get; set; }
public string ReplicationMode { get; set; }
public bool IsMasterRunning { get; set; }
}
public class ReplicationMonitor
{
public async Task<ReplicationMetrics> GetMetrics()
{
var slaveStatus = await _dbContext.QueryAsync<SlaveStatus>(
"SHOW SLAVE STATUS"
);
return new ReplicationMetrics
{
ConnectedSlaves = slaveStatus.Count(),
HealthySlaves = slaveStatus.Count(s => s.IoRunning == "Yes" && s.SqlRunning == "Yes"),
ReplicationLagSeconds = slaveStatus.Min(s => s.SecondsBehindMaster),
IsMasterRunning = true
};
}
}
8.2 复制延迟管理
public class ReplicationLagManager
{
public async Task ManageLag()
{
var metrics = await _monitor.GetMetrics();
if (metrics.ReplicationLagSeconds > 60)
{
await _alertService.SendAlert("复制延迟过高",
$"延迟: {metrics.ReplicationLagSeconds}秒");
await ReduceLag();
}
}
private async Task ReduceLag()
{
await OptimizeBinlog();
await IncreaseReplicationThreads();
await CheckNetwork();
}
private async Task OptimizeBinlog()
{
await _dbContext.Database.ExecuteSqlRawAsync(
"SET GLOBAL binlog_format = 'ROW'"
);
}
private async Task IncreaseReplicationThreads()
{
await _dbContext.Database.ExecuteSqlRawAsync(
"SET GLOBAL slave_parallel_workers = 8"
);
}
}
8.3 自动故障转移
public class FailoverManager
{
public async Task HandleMasterFailure(string masterHost)
{
var slaves = await GetHealthySlaves();
if (slaves.Count == 0)
throw new InvalidOperationException("没有可用的从节点");
var newMaster = SelectNewMaster(slaves);
await PromoteToMaster(newMaster);
await ReconfigureSlaves(newMaster);
await UpdateApplicationConfig(newMaster);
await _alertService.SendAlert("主节点故障转移完成",
$"新主节点: {newMaster.Host}");
}
private SlaveInfo SelectNewMaster(List<SlaveInfo> slaves)
{
return slaves.OrderByDescending(s => s.ReplicationLag).First();
}
private async Task PromoteToMaster(SlaveInfo slave)
{
await _dbContext.Database.ExecuteSqlRawAsync(
$"STOP SLAVE; RESET MASTER;",
new { Connection = slave.Connection }
);
}
private async Task ReconfigureSlaves(SlaveInfo newMaster)
{
var otherSlaves = await GetOtherSlaves(newMaster);
foreach (var slave in otherSlaves)
{
await _dbContext.Database.ExecuteSqlRawAsync(
$"CHANGE MASTER TO MASTER_HOST='{newMaster.Host}'; START SLAVE;",
new { Connection = slave.Connection }
);
}
}
}
九、复制最佳实践
9.1 选择合适的复制模式
根据业务需求选择同步、异步或半同步复制。
9.2 监控复制状态
实时监控复制延迟和健康状态。
9.3 实施自动故障转移
使用MHA、Orchestrator等工具实现自动故障转移。
9.4 避免单点故障
部署多个从节点,确保高可用性。
9.5 定期测试复制
定期测试复制恢复能力,确保数据一致性。
十、总结
数据复制是构建高可用数据库架构的关键技术。同步复制提供强一致性但性能较低,异步复制性能高但只有最终一致性,半同步复制在两者之间取得平衡,多主复制适合多地域部署。合理选择复制策略、监控复制状态、实施自动故障转移,能够构建高可用、高性能的数据存储系统。