复杂度来源-高性能 ################# 技术发展带来了性能上的提升,不一定带来复杂度的提升:: 例如,硬件存储从纸带→磁带→磁盘→SSD,并没有显著带来系统复杂度的增加。 因为新技术会逐步淘汰旧技术,这种情况下我们直接用新技术即可,不用担心系统复杂度会随之提升。 只有那些并不是用来取代旧技术,而是开辟了一个全新领域的技术,才会给软件系统带来复杂度, 因为软件系统在设计的时候就需要在这些技术之间进行判断选择或者组合。 就像汽车的发明无法取代火车,飞机的出现也并不能完全取代火车, 所以我们在出行的时候,需要考虑选择汽车、火车还是飞机,这个选择的过程就比较复杂了, 要考虑价格、时间、速度、舒适度等各种因素。 软件系统中高性能带来的复杂度主要体现在两方面:: 一方面是单台计算机内部为了高性能带来的复杂度 另一方面是多台计算机集群为了高性能带来的复杂度 高性能标准 ========== 高性能没有绝对标准:: 有的业务每秒处理 1000 就是高性能,有的每秒处理 10000 性能都一般, 从业务出发,结合行业经验就可以判断性能要求是否高 单机复杂度 ========== 计算机内部复杂度最关键的地方就是操作系统。计算机性能的发展本质上是由硬件发展驱动的,尤其是 CPU 的性能发展。著名的 “摩尔定律” 表明了 CPU 的处理能力每隔 18 个月就翻一番;而将硬件性能充分发挥出来的关键就是操作系统,所以操作系统本身其实也是跟随硬件的发展而发展的,操作系统是软件系统的运行环境,操作系统的复杂度直接决定了软件系统的复杂度。 .. note:: 操作系统和性能最相关的就是进程和线程。操作系统调度的最小单位就变成了线程,而进程变成了操作系统分配资源的最小单位。 进程间通信方式包括:: 管道、消息队列、信号量、共享存储等 多个 CPU 能够同时执行计算任务,从而实现真正意义上的多任务并行:: 解决方案有 3 种: 1. SMP(Symmetric Multi-Processor,对称多处理器结构) 2. NUMA(Non-Uniform Memory Access,非一致存储访问结构) 3. MPP(Massive Parallel Processing,海量并行处理结构) .. note:: 如果我们要完成一个高性能的软件系统,需要考虑如多进程、多线程、进程间通信、多线程并发等技术点,而且这些技术并不是最新的就是最好的,也不是非此即彼的选择。在做架构设计的时候,需要花费很大的精力来结合业务进行分析、判断、选择、组合,这个过程同样很复杂。举一个最简单的例子:Nginx 可以用多进程也可以用多线程,JBoss 采用的是多线程;Redis 采用的是单进程,Memcache 采用的是多线程,这些系统都实现了高性能,但内部实现差异却很大。 .. note:: redis 的原子性就是因为是单进程 集群的复杂度 ============ 进入互联网时代后,业务的发展速度远远超过了硬件的发展速度。例如:: 2016 年 “双 11” 支付宝每秒峰值达 12 万笔支付 2017 年春节微信红包收发红包每秒达到 76 万个 任务分配-1级 ------------ 1 台服务器演变为 2 台服务器:: 需要增加一个任务分配器,这个分配器: 1. 可能是硬件网络设备(例如,F5、交换机等) 2. 可能是软件网络设备(例如,LVS) 3. 也可能是负载均衡软件(例如,Nginx、HAProxy) 4. 还可能是自己开发的系统。 选择合适的任务分配器也是一件复杂的事情,需要综合考虑性能、成本、可维护性、可用性等各方面的因素 .. image:: https://img.zhaoweiguo.com/knowledge/images/architectures/designs/design_performance1.png 任务分配器需要增加分配算法。例如:: 是采用轮询算法,还是按权重分配,又或者按照负载进行分配。 如果按照服务器的负载进行分配,则业务服务器还要能够上报自己的状态给任务分配器 这个架构只是最简单地增加 1 台业务机器:: 假设单台业务服务器每秒能够处理 5000 次业务请求 那么这个架构理论上能够支撑 10000 次请求 实际上的性能一般按照 8 折计算,大约是 8000 次左右 任务分配-2级 ------------ 2级架构比1级更复杂,主要体现在:: 任务分配器从 1 台变成了多台(对应图中的任务分配器 1 到任务分配器 M), 这个变化带来的复杂度就是需要将不同的用户分配到不同的任务分配器上(即图中的虚线 “用户分配” 部分), 常见的方法包括: 1. DNS 轮询、 2. 智能 DNS、 3. CDN(Content Delivery Network,内容分发网络)、 4. GSLB 设备(Global Server Load Balance,全局负载均衡) .. image:: https://img.zhaoweiguo.com/knowledge/images/architectures/designs/design_performance2.png 机器数量从 3 台扩展到 30 台:: 一般任务分配器数量比业务服务器要少, 这里我们假设业务服务器为 25 台,任务分配器为 5 台 状态管理、故障处理复杂度也大大增加。 任务分解 -------- 业务本身也越来越复杂,单纯只通过任务分配的方式来扩展性能,收益会越来越低:: 业务简单的时候 1 台机器扩展到 10 台机器,性能能够提升 8 倍 (需要扣除机器群带来的部分性能损耗,因此无法达到理论上的 10 倍那么高), 但如果业务越来越复杂,1 台机器扩展到 10 台,性能可能只能提升 5 倍 需要后台架构从逻辑上将各个子业务进行了拆分 .. image:: https://img.zhaoweiguo.com/knowledge/images/architectures/designs/design_performance3.png 任务分解能够提升性能的原因:: 1. 简单的系统更加容易做到高性能 系统的功能越简单,影响性能的点就越少,就更加容易进行有针对性的优化。 而系统很复杂的情况下,首先是比较难以找到关键性能点,因为需要考虑和验证的点太多; 其次是即使花费很大力气找到了,修改起来也不容易, 因为可能将 A 关键性能点提升了,但却无意中将 B 点的性能降低了, 整个系统的性能不但没有提升,还有可能会下降 2. 可以针对单个任务进行扩展 当各个逻辑任务分解到独立的子系统后,整个系统的性能瓶颈更加容易发现, 而且发现后只需要针对有瓶颈的子系统进行性能优化或者提升,不需要改动整个系统,风险会小很多 但并不是划分得越细越好: 划分得太细性能不仅不会提升,反而还会下降, 最主要的原因是如果系统拆分得太细,为了完成某个业务,系统间的调用次数会呈指数级别上升, 而系统间的调用通道目前都是通过网络传输的方式,性能远比系统内的函数调用要低得多 优化方法 ======== 优化方法:: 可以从垂直与水平两个维度来考虑。 垂直维度主要是针对: 单台计算机,通过升级软、硬件能力实现性能提升; 水平维度主要是针对: 集群系统,利用合理的任务分配与任务分解实现性能的提升 单机(垂直维度)优化可包括以下措施:: 增大内存减少 I/O 操作 更换为固态硬盘(SSD)提升 I/O 访问速度 使用 RAID 增加 I/O 吞吐能力 置换服务器获得更多的处理器或分配更多的虚拟内核 升级网络接口或增加网络接口 水平维度可包括以下措施:: 功能分解:基于功能将系统分解为更小的子系统 多实例副本:同一组件重复部署到多台不同的服务器 数据分割:在每台机器上都只部署一部分数据 两种优化方式对比:: 1. 垂直维度方案比较适合业务阶段早期和成本可接受的阶段, 该方案是提升性能最简单直接的方式,但是受成本与硬件能力天花板的限制。 2. 水平维度方案所带来的好处要在业务发展的后期才能体现出来。 起初,该方案会花费更多的硬件成本,另外一方面对技术团队也提出了更高的要求; 但是,没有垂直方案的天花板问题。 一旦达到一定的业务阶段,水平维度是技术发展的必由之路。 因此,作为技术部门,需要提前布局 ,未雨绸缪,不要被业务抛的太远。 例子 ==== 没有搞好架构设计的例子 ---------------------- 前期是没有设计导致性能瓶颈,后期是过度设计导致系统复杂。 之前我们的系统是 all-in 的单系统模式,虽然水平扩展了大量机器,但是仍然存在性能问题,比如类似秒杀之类的活动,几乎会在瞬间把整个系统的数据库连接耗尽,导致其他服务发生卡顿甚至不可用;并且全在一个业务系统中,开发部属效率极低,扩展性也存在问题。 于是我们将系统进行了拆分,起初是按照业务拆分成几个核心系统,同时针对不同业务的负载情况进行了合理的水平扩展,整个系统的性能得到了提升,扩展性得到了保证,并且开发部署效率也得到了极大的提高。 但是随着业务的发展,之前的系统拆分不能满足现有业务,同时随着公司很多老员工的离开,之前的架构设计思路没有人清楚,于是就变成了走一步看一步的推进模式,衍生出了各种独立的服务达 40 个左右,这样系统之间的边界越来越模糊,甚至出现了服务间的循环调用,白白浪费时间。而且一次调用链路过长,发生问题很难定位。 其他 ==== 容量扩展属于性能部分,叫可伸缩性