高可用架构基础:可用性等级、冗余设计与故障隔离
做高可用这么多年,我最大的感触是:很多团队一上来就谈“我们要上多活”“做单元化”,结果连可用性到底该怎么量化、冗余到底要解决什么问题都没搞清楚。这章咱们就扎扎实实地把三个最基础的概念捋清楚——可用性等级、冗余设计思想、故障域与隔离。
什么是可用性?从数字说起
可用性说白了就是一个公式:系统正常服务的时间占比。假设一年8760小时,如果只宕机8.76小时,可用性就是99.9%。这里我习惯用「9」的个数来记——3个9对应年宕机8.76小时,4个9对应52.56分钟,5个9对应5.26分钟。
核心理解:可用性数字不是拍脑袋定的,它直接决定你要花多少钱买机器、做多深的冗余。
不同的可用性等级对应的停机容忍尺度差别很大:
| 可用性等级 | 年停机时间 | 月停机时间 | 典型场景 |
|---|---|---|---|
| 99% | 87.6小时 | 7.3小时 | 内部工具、非关键业务 |
| 99.9% | 8.76小时 | 43.8分钟 | 大多数SaaS产品 |
| 99.99% | 52.56分钟 | 4.38分钟 | 金融交易、实时通信 |
| 99.999% | 5.26分钟 | 26.3秒 | 电信、关键基础设施 |
我曾经遇到一个项目,甲方要求“99.99%”,结果他们说只准备了两台机器做主备切换——切换时间动辄十几分钟,根本跑不到4个9。你想想看,如果把设备故障、网络抖动、版本发布都算进去,99.99%其实是非常苛刻的目标。
为什么水印服务需要高可用?
水印服务听起来简单——给图片或视频打上一个标记。但线上场景里,它往往是生产链路中的关键环节。我记得有一次,一个客户的水印服务部署在单机上,磁盘挂了,整整6个小时所有新生成的素材都没有水印,最后被甲方罚款二十万。
我自己也踩过坑。早期做视频转码系统,水印节点只配了一台,半夜硬件宕机,运维值班人员没看告警,第二天才发现一堆任务失败。从那以后我再也不敢对非关键路径的服务掉以轻心——水印服务一旦不可用,下游数据就可能出现合规风险。
冗余设计思想:把鸡蛋放在多个篮子里
冗余,说白了就是用多个副本对抗单点失效。但冗余不是简单复制,你得想清楚两个问题:冗余的方式有哪些?冗余之后怎么保持一致?
常见的冗余模式有这三种:
- 主备(Active-Standby):一台干活,一台等着。切换需要时间,但实现简单。
- 双活(Active-Active):两台同时处理请求,故障时流量重新分配。资源利用率高,但需要解决数据一致性问题。
- 多活(Multi-Active):跨机房甚至跨地域的多个副本同时服务。最灵活,但网络延迟和一致性成本最高。
| 冗余模式 | 故障切换时间 | 资源利用率 | 一致性复杂度 | 适用场景 |
|---|---|---|---|---|
| 主备 | 秒级~分钟级 | 约50% | 低 | 低成本、容忍短暂中断 |
| 双活 | 毫秒级 | 约80% | 中 | 对RTO要求高的核心服务 |
| 多活 | 秒级(跨地域) | 约90% | 高 | 异地容灾、全球服务 |
我的习惯:对水印这种无状态服务(只处理文件,不保存结果),双活是最划算的选择。因为不用操心数据同步,主要搞定负载均衡和健康检查就行。
故障域与隔离:不要让一颗老鼠屎坏了一锅粥
冗余能解决单点故障,但如果两个副本共享同一个电源、同一条网络,那一个故障就能干掉所有副本。这就引出了一个关键概念——故障域。
故障域是一个物理或逻辑边界,边界内的组件发生故障时,边界外的组件不受影响。比如一台服务器是一个故障域,一个机柜、一个交换机、一个机房也都是不同粒度的故障域。
隔离就是把不同冗余副本放到不同的故障域里。我之前见过一个线上事故:两个节点的网卡连接到同一台ToR交换机,交换机电源爆炸,两个节点同时失联,服务直接挂掉。当时负责架构的同事懊恼地说:“谁想到交换机会炸呢?”但这就是故障域没设计好——两个副本应该分散到不同机柜、不同交换机。
我曾经踩过的坑:第一次做高可用,把主备两台机器放在同一个机架,还用了同一路PDU。机房夏天跳闸,两台机器一起断电,故障转移完全无效。那次之后我规定:任何服务的主备或双活节点,必须跨机架、跨PDU。
故障域的粒度要根据你的成本和服务等级来定。对水印服务来说,至少要做到:
- 同机房内跨机架(应对机架级故障)
- 同城跨机房(应对机房级故障,比如空调漏水、网络割接)
- 极端场景下可以考虑异地(应对城市级灾害)
一张图看懂高可用基础概念
下面这张图把可用性等级、冗余设计、故障域与隔离的关系串起来了。水印服务的可用性目标决定了你选多少个9,而9的个数反过来决定你要做多深的冗余、多大粒度的故障隔离。
实战中的避坑指南
小技巧:在设计冗余时,可以用一个简单的Python函数快速估算不同可用性等级下的允许中断时长,辅助决策:
def downtime_per_year(nines):
return 365 * 24 * 60 * 60 * (1 - nines/100)
print(f"99.9% = {downtime_per_year(99.9):.0f}秒")
print(f"99.99% = {downtime_per_year(99.99):.0f}秒")
特别提醒:故障域隔离不是有了就万事大吉。我见过一个团队把双活节点放在两个机架,但两个机架共用同一个列头柜电源——列头柜一跳闸,两个节点一起跪。设计完成后一定要物理走查,确保每个故障域真的隔离了共享资源。
好了,这一章的基础概念打牢了,后面我们讲具体架构的时候才能心里有数。记住:先定可用性目标,再选冗余模式,最后用故障域隔离把风险分散开。这三步走稳了,高可用架构的根就扎实了。