同城双活架构设计:流量分配与数据同步
想象一下这个场景:
你的系统在 A 机房运行,突然——停电了。
一个小时后恢复,但你的用户已经无法访问服务一个小时了。
如果你的系统做了同城双活,只需要几分钟切换,用户几乎无感知。
这就是同城双活的价值。
双活 vs 主备
很多人分不清双活和主备的区别:
- 主备:主机房承载所有流量,备机房待机。故障时切流量。
- 双活:两个机房都承载流量,故障时,另一个机房瞬间接管。
主备架构:
┌─────────────┐ ┌─────────────┐
│ 主机房 │────▶│ 备机房 │
│ (100%流量) │ │ (待机) │
└─────────────┘ └─────────────┘
双活架构:
┌─────────────┐ ┌─────────────┐
│ 机房 A │◀───▶│ 机房 B │
│ (50%流量) │ │ (50%流量) │
└─────────────┘ └─────────────┘双活的优势:
- 容灾效果更好:一个机房挂了,另一个机房瞬间接管
- 资源利用率高:两个机房都在工作,没有闲置资源
- 切换速度快:不需要等待备机房启动
同城双活的优势
为什么是同城?
- 延迟低:同城机房之间网络延迟通常在 1-5ms
- 网络稳定:同城网络质量好,丢包率低
- 成本可控:比异地成本低很多
同城双活的 RTO(恢复时间目标)可以做到分钟级,RPO(恢复点目标)接近 0。
流量分配
DNS 智能解析
最常用的流量分配方式:
java
// DNS 配置示例
// 正常情况下:
// 北京用户 → 北京机房 A
// 上海用户 → 上海机房 B
// 故障情况下:
// 北京用户 → 上海机房 B(DNS 切换)DNS 智能解析需要:
- EDNS 协议:获取用户真实 IP(而非递归 DNS IP)
- 健康检查:实时检测机房健康状态
- 负载均衡策略:轮询、加权、最优路径
VIP 漂移
VIP(Virtual IP)在两个机房之间漂移:
正常情况下
主机房 VIP: 10.1.1.100
备机房 VIP: 无
故障情况下
主机房 VIP: 无
备机房 VIP: 10.1.1.100VIP 漂移通常配合网络设备(VRRP 协议)实现。
数据同步
同城双活的核心问题是:两个机房的数据如何保持一致?
同步复制
两个机房同时写入,数据强一致:
java
// 双写逻辑
@Transactional
public void transfer(Account from, Account to, BigDecimal amount) {
// 同时写入两个机房
dbA.update(from, -amount);
dbB.update(from, -amount);
dbA.update(to, amount);
dbB.update(to, amount);
}问题:
- 延迟增加:双写比单机写慢
- 一致性风险:如果一个机房写入成功、另一个失败,数据不一致
- 复杂度高:需要分布式事务支持
异步复制
主机房写入成功,异步复制到备机房:
java
// 异步复制
public void transfer(Account from, Account to, BigDecimal amount) {
// 主机房写入
dbA.update(from, -amount);
dbA.update(to, amount);
// 异步复制到备机房
asyncReplication(from, -amount);
asyncReplication(to, amount);
}问题:
- 数据可能丢失:主机房写入后、备机房复制前,主机房挂了
- RPO > 0:恢复时会有数据丢失
延迟双写
主机房写入,备机房读取。当数据不一致时,用主机房数据覆盖:
java
// 延迟双写策略
public void transfer(Account from, Account to, BigDecimal amount) {
// 主机房写入
Transaction txA = dbA.beginTransaction();
dbA.update(from, -amount);
dbA.update(to, amount);
txA.commit();
// 备机房异步写入(延迟几百毫秒)
delayQueue.add(new WriteTask(from, -amount));
delayQueue.add(new WriteTask(to, amount));
}挑战与权衡
网络延迟
两个机房之间有网络延迟,通常 1-5ms。
对于大部分业务,这个延迟可以忽略。
但对于强一致性要求的场景(如金融交易),需要考虑:
- 跨机房事务超时设置要更大
- 避免跨机房强一致性操作
数据一致性
双活架构下的一致性问题是复杂的:
- 最终一致:异步复制,保证最终一致
- 强一致:同步复制,性能差
- 业务妥协:接受短暂不一致,通过补偿机制处理
流量切换
故障时如何切换流量?
- DNS 切换:修改 DNS 记录,用户本地 DNS 缓存过期后生效(5 分钟-24 小时)
- VIP 漂移:秒级切换,但需要网络设备支持
- 负载均衡器:流量在负载均衡层切换
面试追问方向
- 同城双活和异地多活的区别是什么?(答:同城延迟低,适合 RTO/RPO 要求高的场景;异地成本高,但可以应对更大的灾难)
- 如何保证双机房的数据一致性?(答:最终一致、同步复制、业务妥协等方案,根据业务选择)
- 双活架构下如何做单元化?(答:按用户 ID 路由,同一用户的数据路由到同一机房)
- 机房故障时如何快速切换?(答:VIP 漂移 + 健康检查 + 自动化脚本)
小结
同城双活是提高系统可用性的重要手段:
- 两个机房同时服务:提高资源利用率,加快故障切换
- 流量分配:DNS 智能解析 + VIP 漂移
- 数据同步:根据业务需求选择同步/异步
- 快速切换:自动化健康检查 + 流量切换
同城双活不是银弹,它增加了架构复杂度。需要根据业务需求(RTO/RPO)和成本权衡是否采用。
