二、高可用设计原则:消除单点故障、冗余设计、故障自动转移、可回滚性、可观测性

聊到高可用,很多人第一反应就是「多部署几台机器」。嗯,这话没错,但远远不够。我做了十几年架构,见过太多「看似高可用、一挂全完蛋」的案例。说白了,高可用不是靠堆机器堆出来的,而是靠一套严谨的设计原则撑起来的。

今天我就把这五个核心原则掰开揉碎了讲。你想想看,任何一个生产系统,只要把这五点做到位了,99.9% 的故障场景都能扛得住。

2.1 消除单点故障:最基础也最容易被忽略

什么叫单点故障?就是系统里某个组件挂了,整个服务就瘫了。我见过最离谱的一次,某公司核心数据库做了主从,结果网络交换机是单台——交换机一坏,全库不可用。这就是典型的「只盯着应用层,忘了基础设施层」。

消除单点故障,要分三个层面来排查:

  • 硬件层:电源、网卡、磁盘、交换机,任何物理设备都要冗余
  • 软件层:进程、线程、连接池、缓存,不能只有一个实例
  • 架构层:网关、注册中心、配置中心,这些核心组件必须集群化

核心检查清单

  • 每个服务至少 2 个实例,部署在不同物理机
  • 数据库、缓存、消息队列全部主从或集群
  • 负载均衡器自身也要做 HA(比如 Keepalived + Nginx)
  • DNS 解析不要只配一个 IP

⚠️ 我曾经踩过的坑

有一次做支付系统,所有服务都做了双活,唯独忘了「定时任务调度器」是单点的。结果调度器所在机器宕机,所有对账任务停摆 6 小时。从那以后,我要求所有定时任务必须支持分布式调度(比如 XXL-Job 或 Elastic-Job)。

2.2 冗余设计:不是简单的「多复制一份」

冗余设计听起来简单——多部署几台不就完了?其实没那么简单。我个人的习惯是,冗余要分「热备」和「冷备」两种场景来考虑。

冗余类型 切换时间 适用场景 成本
热备(Active-Active) 秒级 核心交易、实时业务
温备(Active-Standby) 分钟级 报表、后台任务
冷备(Cold Standby) 小时级 灾备恢复、历史数据

这里有个关键点:冗余不只是「多一份数据」,还要考虑「流量分发」和「数据一致性」。举个例子,你做了双活,但两个机房之间的网络延迟如果超过 10ms,那写冲突就会频繁出现。我在项目中遇到过这种情况,最后不得不改成「同城双活 + 异地灾备」的混合模式。

💡 我的建议

冗余设计时,先问自己三个问题:

  1. 冗余的组件之间如何同步数据?(同步/异步/最终一致)
  2. 如果冗余节点全部故障,有没有降级方案?
  3. 冗余带来的成本增加,业务是否愿意买单?

2.3 故障自动转移:别等人来修

你想想看,凌晨三点数据库主库挂了,你是等着运维起床手动切,还是系统自己搞定?我个人的经验是:凡是需要人工介入的故障恢复,都不叫高可用

故障自动转移的核心是三个步骤:

  • 检测:心跳、健康检查、超时阈值,要能快速发现故障
  • 决策:确认故障后,选择哪个节点接管,要不要做数据校验
  • 执行:切换路由、更新注册中心、触发告警

以 Redis 哨兵模式为例,自动转移的配置大概是这样的:

# sentinel.conf
sentinel monitor mymaster 192.168.1.10 6379 2
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 60000
sentinel parallel-syncs mymaster 1

这里有个容易忽略的细节:down-after-milliseconds 设得太短容易误判,设得太长又影响恢复速度。我一般建议设成 5-10 秒,配合三次重试机制,既避免抖动又保证及时切换。

⚠️ 注意

自动转移不是万能的。我曾经遇到过一个案例:主库磁盘满了,自动切换到从库,但从库的磁盘也快满了——结果切换完又挂了。所以自动转移一定要配合「容量监控」和「限流降级」,否则就是从一个坑跳到另一个坑。

2.4 可回滚性:给系统留条后路

做架构设计时,很多人只想着「怎么往前走」,很少想「怎么退回来」。但现实是——发布出问题、配置改错了、数据写坏了,这些事每天都在发生。没有回滚能力,你就只能干瞪眼。

可回滚性要覆盖三个层面:

  • 代码回滚:版本管理、灰度发布、蓝绿部署
  • 数据回滚:数据库快照、事务日志、补偿机制
  • 配置回滚:配置中心的历史版本、一键回退

我个人的习惯是,每次发布前都要确认两件事:

  1. 如果新版本挂了,回滚需要几步?
  2. 回滚过程中,数据一致性怎么保证?

一个实用的回滚策略

数据库变更一定要用「增量脚本 + 回滚脚本」成对管理。比如:

-- V1.0.1__add_user_age.sql
ALTER TABLE user ADD COLUMN age INT DEFAULT 0;

-- V1.0.1__rollback.sql
ALTER TABLE user DROP COLUMN age;

这样出了问题,直接执行回滚脚本,不用临时写 SQL,避免二次事故。

2.5 可观测性:没有数据,你什么都不知道

最后这条原则,我觉得是最容易被低估的。很多人觉得「系统能跑就行」,但真正出故障的时候,你连问题出在哪都不知道,那还谈什么高可用?

可观测性包含三个支柱:

维度 工具举例 关注点
Metrics(指标) Prometheus + Grafana CPU、内存、QPS、延迟、错误率
Logging(日志) ELK / Loki 错误堆栈、业务日志、审计日志
Tracing(链路) Jaeger / Zipkin 请求链路、耗时分布、依赖关系

我建议每个微服务至少要暴露以下指标:

  • 请求总数、成功数、失败数
  • 平均延迟、P99 延迟
  • 当前活跃连接数
  • 最近 5 分钟的错误率趋势

💡 一个小技巧

告警不要设太多,否则就是「狼来了」。我一般只设三个级别的告警:

  • P0:服务不可用,立即响应(比如错误率 > 5%)
  • P1:性能劣化,需要关注(比如 P99 延迟翻倍)
  • P2:资源预警,日常排查(比如磁盘使用率 > 80%)

这样团队不会被告警淹没,真正出问题时也能快速定位。

好了,这五个原则讲完了。你发现没有?它们其实是环环相扣的——消除单点故障是基础,冗余设计是手段,故障自动转移是保障,可回滚性是兜底,可观测性是眼睛。缺了任何一个,高可用都是空中楼阁。

下一章我会讲具体的架构模式,比如「同城双活」和「两地三中心」到底怎么落地。到时候咱们再细聊。