找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖
Claude、GPT 海外模型 API 接入Claude skills 从入门到精通 吴恩达亲授 AI Agent 核心技能2026 瞪哥公务员考试全攻略 行测申论一站式系统备考
Agent 文心智能蒸馏模型实战 90G 课程智泊 AI 大模型训练营 基于 LangChain 的 RAG 与提示工程实战构建企业级 AI 大脑:大模型微调与 RAG / Agent 全栈实战

6243

积分

0

好友

791

主题
发表于 前天 00:30 | 查看: 0| 回复: 0

面试官问:“什么是异地多活?”

这题要是只回答一句“多个机房同时提供服务,提高系统可用性”,基本等于没答。

我更关心的是后面那半句:两个机房都在接流量,数据到底怎么写?

这才是异地多活真正麻烦的地方。

假设一个交易系统部署在北京和上海。

北京机房有:

Gateway
Order Service
Redis
MySQL
MQ

上海也有完整的一套。

正常情况下,北京用户可以进北京机房,上海用户进上海机房。两个机房都真正处理业务,不是一个干活、另一个机器开着吃灰。

这叫“多活”。

如果北京机房整体断电,流量切到上海,上海还能继续提供服务,这才体现出异地部署的价值。

但这里有个很容易混的概念。

北京主机房提供服务,上海只同步数据库,平时完全不接业务,这更接近异地灾备或者主备,不算真正意义上的多活。

我面试别人时,一般会继续追一句:

“那订单数据怎么办?北京上海两个库都能写吗?”

如果直接回答“双向同步”,我反而会有点担心。

因为数据库双写远没有画架构图那么轻松。

比如用户刚在北京修改收货地址:

北京:address = 上海浦东

网络还没同步过去,他的下一个请求被调度到了上海,而上海数据库里还是:

上海:address = 北京朝阳

用户一刷新,地址又变回去了。

更麻烦的是两个机房同时修改同一条数据。

到底听谁的?

靠时间戳?

机器时间未必绝对一致。

靠版本号?

那冲突怎么合并?

所以做异地多活,我一般不会一上来就追求“所有数据所有机房随便写”。

这种设计看着自由,后面处理数据冲突的时候一点都不自由。

比较常见的做法是给数据划归属。

比如按用户 ID 分片:

public final class RegionRouter {

    private static final int BUCKETS = 1024;

    public String locate(long userId) {
        int slot = Math.floorMod(Long.hashCode(userId), BUCKETS);

        if (slot < 512) {
            return "cn-beijing";
        }
        return "cn-shanghai";
    }
}

假设一个用户归北京,那么他的订单、账户之类的核心写请求,尽量固定落北京。

上海可以保留数据副本,也可以承担读请求,但别让同一份核心数据平时在两个地方乱写。

路由层大概会有这样的判断:

public OrderResult create(OrderCommand cmd) {
    String home = regionRouter.locate(cmd.userId());

    if (!localRegion.equals(home) && regionState.isHealthy(home)) {
        return remoteOrderClient.forward(home, cmd);
    }

    return orderWriter.create(cmd);
}

代码不复杂,麻烦的是这个规则必须从入口一直贯彻到数据库、缓存、MQ。

否则 Gateway 把用户路由到北京,订单写北京库,结果库存服务又随机跑到了上海,前面的归属规则就白做了。

还有一个坑是跨机房调用。

北京 Order Service 每创建一个订单,都同步调用上海的会员服务:

北京 Order
   |
   | 跨机房 RPC
   v
上海 Member

这种架构我第一眼就不太信。

机房内部一次 RPC 可能很快,跨城之后网络延迟、抖动、专线故障全进来了。调用链再长一点,一个请求跨机房来回跑几趟,接口耗时很容易被拖起来。

所以异地多活通常还有一条挺重要的原则:

请求尽量在一个单元内闭环。

能本地完成,就别跨机房同步调用。

需要同步的数据,通过 MQ、CDC、Binlog 之类异步过去。

例如订单状态同步,消费端首先要考虑的甚至不是“怎么消费”,而是幂等:

@Transactional
public void apply(OrderSyncEvent event) {
    if (syncRecordDao.exists(event.eventId())) {
        return;
    }

    orderReplicaDao.saveOrUpdate(
        event.orderId(),
        event.status(),
        event.version()
    );

    syncRecordDao.markDone(event.eventId());
}

因为跨机房复制出现重试、重复消息很正常。

不做幂等,故障切换还没开始,数据先被自己重复处理坏了。

那机房真挂了怎么办?

比如北京彻底不可用。

这里也不是 DNS 一切,上海接住流量就结束了。

至少得判断几件事:

北京最后一批数据有没有同步完成?

上海的数据落后多少?

那些原本属于北京的数据,现在是否允许上海接管写权限?

北京恢复以后,新增数据怎么回同步?

双边会不会同时认为自己是主?

最后这个最危险。

两个机房网络断了,但机器都活着,北京觉得上海死了,上海也觉得北京死了,然后两边同时开放写入。这就是典型的脑裂。

所以异地多活真正难的从来不是“多部署几套服务”。

机器复制一套不难。

难的是流量路由、数据分片、跨机房同步、故障检测、写权限转移、脑裂保护,以及故障恢复之后怎么把数据重新收回来。

面试里如果让我压缩成一句话,我会这么回答:

异地多活是把完整业务能力部署到多个地域,并让多个地域同时承载真实流量;它解决的是单机房故障下的业务连续性,但真正的设计重点不是部署,而是怎么控制数据写入和故障切换。

答到这里,面试官如果继续问“双活数据库怎么保证一致性”,这题才算真正开始。类似这类架构问题,在云栈社区也经常被拿出来讨论。




上一篇:MySQL varchar(50)和varchar(500)到底差在哪?存储、索引与Java校验详解
下一篇:OpenContext 本地记忆库:不用换 Codex/Claude,也能给 AI Agent 共享长期记忆
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-9-25 02:10 , Processed in 1.602175 second(s), 46 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表