高可用架构设计

835 阅读6分钟

设计高可用的软件架构,是每个互联网软件开发工程师的追求。尤其对于在线交易系统、在线广告系统而言,服务的可用性会直接影响商业变现。随着互联网技术在工业界的广泛使用,各大互联网公司在各自的业务领域内,沉淀了成熟的高可用架构方案。那么究竟什么是高可用?高可用架构该如何设计呢?

度量

首先需要了解下什么是可用性以及如何度量可用性。对于一个交互式IT产品,是否可用是看用户能否用该产品完成他的任务。可用性就是在某个考察时间内,系统能够正常运行的概率或时间占有率的期望值。对于可用性等级,业内一般用n个9来描述,如下所示。


服务处于不可用状态的时间称为故障时间。可用性每提高一个等级,故障时间就要降一个数量级。从天到时到分,相对来说比较容易实现。再往后每提高一个等级,将付出成百上千倍的努力。大型网站服务通常至少做个4个9,做到5个9及以上就比较困难了。不仅要解决技术挑战,还要面对极大的成本压力。对于网站核心服务,会尽可能做到5个9,而非核心服务4个9,甚至3个9也可以接受。做技术决策时必须考虑经济账

方法

那如何做到高可用呢?方法很简单:冗余。通俗讲,就是双保险机制。背后的理论基础是概率论。假设某个服务的可用性是99%(故障率1%),那么两个服务的可用性就是1-0.01*0.01=99.99%。可以看到,冗余对可用性的提升是指数级的。再冗余一个服务,可用性就达到6个9了。哇!提高可用性等级似乎好简单啊!?

限制

真这么简单吗?当然不是。别忘了CAP定理

CAP是Consistency(一致性)、Availability(可用性)、Partition-tolerance(分区容错性)三个词的首字母缩写。CAP定理是加州大学的计算机科学家 Eric Brewer 提出的:CAP三者不可兼得,提高其中任意两者的同时,必然要牺牲第三者。(延伸阅读《分布式系统缘起及理论》


用冗余提升可用性,本质上是在追求AP。冗余越多,解决C的成本就越高。其中最大的成本是时间成本,时间成本是技术上不可接受的。程序员提起高可用系统,经常会再加一个词『高并发』。高并发就体现了技术对时间的追求。正是这些不可兼得的矛盾,才让架构师们在面对不同业务场景时,需要做不同的技术取舍。

设计

冗余的架构设计有三种模式:双主(Master & Master)、主备(Master & Co-Master)和主从(Master & Slave)。


双主模式中,两台服务是平等关系,同时对外提供读写服务,客户端任选一台即可。双主模式是可用性最好的,但是这种架构的一致性处理比较困难,需要两台服务进行双向数据同步。一旦它们之间的通信断开,就形成了网络分区,这种分区会带来脑裂(brain-split)问题,并且系统对此无解,必须人工介入。所以在架构设计时极少选择双主模式。

主备模式中,两台服务不再是平等关系。主服务承担所有的读写请求,备服务只有在主服务不可用时才取而代之。主备服务之间虽然也存在两个方向的数据同步,但跟双主模式不同,它们不会同时发生。正常情况下只存在主向备同步数据。主不可用的时间段内,数据会写到备服务。当主恢复后,才需要由备向主同步数据。在此期间,会双写数据到主备节点,防止主同时再向备同步数据。主备架构比较容易实现,缺点是备服务在绝大部分时间是一种资源浪费。一般数据库系统在部署时会考虑主备架构。

主从模式其实不是主要解决高可用问题的,更多的是为了实现读写分离,来解决高并发问题。实际场景中通常不是一主一从,而是一主多从架构,因为大部分应用都是的读多写少。主节点处理写请求,从节点处理读请求。由于存在多从,读服务的可用性远高于写服务。另外,写服务会存在单点故障。这个问题可以通过集群动态选主来解决:当主节点不可用时,集群自动选出一台新的主节点。基于zookeeper,动态选主很容易实现(延伸阅读《Zookeeper理论&应用&实操》)。不过动态选主在MySQL集群架构中不会使用,原因是主从同步数据必须在部署时配置好,切换了主节点还是需要运维人工介入修改配置并重启服务。

案例

上面是抽象的架构设计,实际场景的架构要更复杂,会组合使用主备和主从架构。下图是国内某互联网公司MySQL集群的简化版架构方案。


DBProxy位于应用与数据库之间,屏蔽底层数据库架构对应用造成的影响,为应用提供透明的高性能代理服务。DBProxy中会实现读写分离、失败重连、负载均衡、安全认证、连接池、表路由、表Hash等功能。

多个MySQL集群是为了实现分库。不同集群的数据是不同的,dbproxy会根据路由规则,找到存储在不同分库上的表。

单个MySQL集群中组合使用了主备和主从架构。使用主备是为了解决前面提到的写数据单点故障问题。正常情况下,写主读从,主同时向备从节点同步数据。当主不可用时,读写请求全部落在备节点上,备节点没有从节点。当主恢复后,备向主同步数据,在此期间,主备会双写数据。同步完数据后,再恢复到正常模式。为避免主备在双写数据生成相同的自增ID,会让主备ID增长步长设为2,主库奇数增长(1,3,5,7,...),备库偶数增长(2,4,6,8,...)。


延伸阅读