复杂度来源:高性能
软件系统中高性能带来的复杂度主要体现在两方面,一方面是单台计算机内部为了高性能带来的复杂度;另一方面是多台计算机集群为了高性能带来的复杂度。
单机复杂度
计算机内部复杂度的核心在于操作系统。随着硬件性能(尤其是 CPU)的发展,操作系统逐渐演变为一个复杂的环境,直接影响着软件系统的复杂度。
最早的计算机没有操作系统,只能进行简单的输入、计算和输出。为了提升效率,批处理操作系统应运而生。批处理系统通过事先写好的指令清单,即“任务”,来让计算机自动执行任务,从而减少人为干预,提升处理效率。然而,批处理系统仍然只能一次处理一个任务,导致 CPU 在等待 I/O 操作时处于空闲状态。
为了解决这个问题,进程的概念被引入。进程允许操作系统同时管理多个任务,通过分时处理的方式,使多个进程看似在并行运行。然而,进程之间互不干涉,无法直接通信,为此,进程间通信机制(如管道、消息队列、信号量、共享存储等)被设计出来。
虽然多进程提升了效率,但进程内的任务依然是串行执行的。为了解决这一问题,线程的概念应运而生。线程是进程内部的子任务,多个线程可以共享同一份进程数据,并同时执行。为保证数据一致性,互斥锁机制也被引入。此时,操作系统的调度单位变成了线程,而进程成为了资源分配的单位。
尽管多进程和多线程让任务处理更高效,但本质上还是分时系统,并未实现真正的并行。为此,硬件层面引入了多处理器结构。常见的结构包括:SMP(对称多处理器结构)、NUMA(非一致存储访问结构)、MPP(海量并行处理结构),其中 SMP 是目前最常见的多核处理器方案。
操作系统的这些技术点(如多进程、多线程、进程间通信、多线程并发等)并不是独立选择的,而是需要在架构设计中综合考量和组合,增加了系统设计的复杂性。
集群的复杂度
随着业务的发展速度远超硬件性能的提升,单台计算机已经无法支撑互联网时代的高负载需求。为了应对这种情况,必须采用机器集群来提升性能。例如:
- 2016 年“双 11”支付宝的每秒峰值支付达 12 万笔
- 2017 年春节微信红包的每秒收发量达到 76 万个。
在集群中,为了让多台机器协同工作以实现高性能,架构设计变得非常复杂。常见的集群复杂性主要体现在两方面:任务分配和任务分解。
1. 任务分配
任务分配是指每台机器都能处理完整的业务,不同的任务被分配到不同的机器上执行。随着服务器从 1 台扩展到 2 台,架构复杂性显著提升。
主要体现在以下几个方面:
- 需要引入任务分配器,它可能是硬件设备(如 F5、交换机)或软件(如 Nginx、HAProxy),甚至是自研系统。选择合适的任务分配器时,需要权衡性能、成本、可维护性、可用性等因素。
- 任务分配器与业务服务器之间的连接管理需要考虑连接建立、检测、断开的处理方式。
- 需要设计合适的分配算法,如轮询、按权重分配,或者按服务器负载分配。
如果业务量继续增长,例如从 1 万次每秒到 10 万次每秒,单个任务分配器会成为瓶颈,必须将其扩展为多台,这时的架构又会演变成这个样子。
此时架构进一步复杂化:
- 任务分配器从 1 台扩展到多台,这要求将用户请求分配到不同的任务分配器上(即图中的虚线“用户分配”部分),常用方法包括 DNS 轮询、智能 DNS、CDN、GSLB(全局负载均衡)等。
- 任务分配器与业务服务器的连接从简单的“1 对多”变为“多对多”网状结构。
- 机器数量增加,状态管理和故障处理的复杂度也大幅提升。
2. 任务分解
当业务复杂度进一步提高时,单纯通过任务分配扩展性能的收益开始降低。为了进一步提升性能,需要采用任务分解的方式,将复杂的业务系统拆分为多个小而简单的子系统。以微信的后台架构为例
通过上面的架构示意图可以看出,微信后台架构从逻辑上将各个子业务进行了拆分,包括:接入、注册登录、消息、LBS、摇一摇、漂流瓶等子系统。
任务分解提升性能的原因主要有以下几点:
- 简单系统更容易优化:功能简单的系统,性能瓶颈更易发现,优化也更有针对性。而复杂系统则难以找到关键性能点,即使找到了,优化时也容易产生连锁反应,导致性能下降。
- 子系统独立扩展:各个子系统独立后,可以针对性地优化或扩展性能瓶颈。例如,当注册登录系统性能出现瓶颈时,只需优化这一部分,其他子系统不必改动。
然而,任务分解并不是越细越好。过度细分可能导致系统间的通信开销急剧上升。随着子系统数量增加,系统间的调用次数成倍增长,而网络通信的性能远低于系统内部的函数调用。
例如,当系统拆分为 100 个子系统时,为完成一次用户请求,可能需要进行 99 次系统间的网络请求。
举例来说,假设网络传输每次请求耗时 1ms,而业务处理耗时 50ms,那么拆分为 2 个子系统时,总处理时间为 51ms;拆分为 100 个子系统时,总耗时则达到 149ms。虽然系统拆分提升了性能,但它仍有一定的上限,无法突破业务逻辑本身的性能瓶颈。
因此,任务分解的粒度非常关键,过细或过粗都会带来不良后果。设计架构时,需要在系统复杂度和性能提升之间找到平衡点。
总结
高性能系统的设计无论是单机性能的优化,还是集群架构的扩展,都会带来复杂度的增加。单机层面的多进程、多线程、进程间通信等技术,以及集群中的任务分配和任务分解策略,都是在提升性能的同时,增加了系统的设计和实现复杂度。
在实际的架构设计中,必须结合具体业务需求,权衡各种技术的优缺点,找到最合适的方案。高性能的系统设计是一门平衡艺术,需要不断权衡性能、复杂度和成本。
- 感谢你赐予我前进的力量