软件架构六大复杂度来源
结合当下云原生、微服务、分布式最新实践,通俗易懂解读,附带案例、原理、示意图说明,适合架构入门、技术评审、学习笔记。
前言
架构设计本质目的:解决软件系统复杂度带来的问题 。
软件的复杂度主要来自 6 个维度:高性能、高可用、可扩展性、低成本、安全、规模 。很多新手做架构容易陷入误区:一味追求新技术、追求高大上的微服务、分布式,但忽略复杂度本身。所有架构方案都要为复杂度做取舍,没有银弹,只有合适的方案。
一、复杂度来源 1:高性能
追求性能是技术发展底层驱动力,性能越高,实现手段的复杂度就越高。高性能带来的复杂度分为两大块:单机内部高性能 、多机集群高性能 。
1.1 单机内部高性能的复杂度
单机高性能本质由硬件 CPU 驱动,操作系统把硬件能力释放给上层应用。发展历史:
- 无操作系统:人工输入指令,CPU 大量等待人工输入,效率极低。
- 批处理系统:预录入一批任务,机器自动执行;缺点:I/O 读写的时候 CPU 空闲浪费。
- 进程 :一个进程代表一个任务,CPU 分时复用,宏观上实现 "并行";进程拥有独立内存,进程之间隔离;为了进程之间互通,衍生管道、消息队列、信号量、共享内存等进程间通信手段。
- 线程 :一个进程内部多个线程,进程是资源分配最小单元,线程是 CPU 调度最小单元 ,线程共享进程内存;为保证多线程数据不出错,引入互斥锁、并发控制,带来锁竞争、死锁等一系列复杂度。
- 多核 CPU:SMP 对称多处理器(普通多核 CPU)、NUMA、MPP 三种架构,实现真正物理并行。
现实案例:同样实现高性能,选型差异巨大
- Nginx:多进程模型
- JBoss:多线程模型
- Redis:单进程单线程(避免多线程锁的复杂度,靠单线程 + IO 多路复用,一台机器部署多个实例利用多核)
- Memcache:多线程模型
现代补充:云原生时代,协程(Go goroutine)进一步降低切换开销,但同样带来协程调度、异常处理、泄露等新复杂度。
1.2 集群高性能带来的复杂度
单机硬件有物理天花板,互联网业务流量远超单机上限,需要集群。集群提升性能两种手段:任务分配、任务分解 。
(1)任务分配(水平副本)
通俗理解:同样一份业务程序,复制多份实例,请求分发到不同机器处理。示意图:单服务器 → 增加任务分配器(负载均衡),后端多台业务服务器。

带来复杂度:
- 引入任务分配器(Nginx、LVS、F5、HAProxy、DNS 轮询、GSLB);分配器本身会成为新瓶颈,分配器也要集群化。
- 连接管理:连接建立、心跳探测、故障断开、重试逻辑。
- 分配算法:轮询、权重、负载感知;负载感知还需要业务服务器上报自身状态。
现代对应:K8s Deployment 多副本 + Service 负载均衡,就是典型任务分配模式。注意:理论性能线性提升,但实际会有网络、同步损耗,通常只能达到理论 80% 左右。
(2)任务分解(业务拆分)
通俗理解:把一个大而全的单体系统,按业务功能拆成多个不同子系统,每个子系统独立部署。示例:微信后台,拆分为接入服务器、注册登录、消息、LBS、摇一摇、漂流瓶等独立子服务。

任务分解为什么可以提升性能:
- 系统功能简单后,更容易定位性能瓶颈、针对性优化;单体中一个模块慢查询会拖累整个系统。
- 可以针对性扩容 :哪个子系统压力大,就只扩容哪个子系统,不需要整体扩容。
⚠️ 重要陷阱:不是拆分越细越好! 拆分过细,原本进程内函数调用,变成跨机器网络调用,网络 RTT 开销急剧上涨,调用链变长,整体性能反而下降。
书中举例:业务处理耗时 50ms,每次网络交互 1ms;拆成 2 个子系统总耗时 51ms;拆成 100 个子系统串行调用总耗时 149ms。现实痛点:很多公司过度微服务,拆分几十个服务,一次业务跨十几、二十次网络调用,超时、链路追踪、事务一致性全部变成难题。
现代微服务本质就是任务分解,粒度把控是架构师核心能力,拆分收益和网络损耗做权衡 。
高性能小结
- 单机:多进程 / 多线程 / 协程、并发锁、IO 模型;
- 集群:任务分配(多副本负载均衡)、任务分解(业务拆分);
- 核心权衡:性能收益 vs 新增复杂度(锁、网络、一致性)。
二、复杂度来源 2:高可用
高可用定义:系统无中断地执行功能的能力 。硬件会坏、软件有 bug、还会遇到断电、光缆挖断等灾难。✨高可用核心手段:冗余 。注意区分:
- 高性能加机器:目标是扩展处理能力 ;
- 高可用加机器:目标是冗余备份,防止单点故障 。
2.1 计算高可用(业务逻辑服务)
计算服务是无状态,同一输入任意实例输出结果一致。架构:任务分配器 + 多台计算服务器。

分配策略选择:1 主 3 备、2 主 2 备、全主 0 备等。例如 Zookeeper 是 1 主多备;Memcached 全主 0 备。复杂度点:分配器本身的高可用、连接健康检测、故障摘除、切换逻辑。
2.2 存储高可用(最大难点)
存储和计算本质不同:数据要在多节点之间传输复制,网络存在延迟、丢包、中断。
举例:北京机房存钱 1 万元,网络延迟,上海机房还没同步;用户查询路由到上海,余额看不到新增 1 万元,带来业务故障。

存储高可用难点不是备份数据,而是规避数据不一致带来业务伤害 。理论依据:CAP 定理,分布式存储不能同时满足一致性、可用性、分区容错,架构设计必须取舍。
现实案例:银行核心优先 CP(强一致);普通互联网社交产品优先 AP(最终一致性)。
2.3 状态决策:高可用的底层基础
冗余集群必须能够判断节点死活,做故障切换。但是状态判断很难做到 100% 准确,分三种模式:
独裁式(单决策者) 有一个独立决策者,所有节点上报状态。优点:决策唯一不会混乱;致命缺点:决策者本身单点故障,整个集群决策瘫痪。

协商式(主备模式) 两台机器互相通信协商,选出主机,另一台备机。痛点:心跳网络断开,备机无法区分:是主机挂了?只是网络断了?
如果直接升级备机 → 双主(脑裂风险);
如果不升级 → 主机真故障,整个系统无主机不可用。增加多条心跳链路只能降低概率,不能根除。
民主式(投票选举,Raft/Paxos) 集群所有节点平等,通过投票多数派做出决策,Zookeeper、etcd 就是该模式。风险:脑裂 ,网络分区集群分裂成 2 个子集群,各自选出主节点。解决办法:投票必须超过半数节点;代价:部分节点全部故障,哪怕剩余节点是好的,如果凑不齐多数派,系统直接停止服务,牺牲可用性换一致性。

现代云原生实践:etcd 使用 Raft 民主投票;数据库主备是协商式;部分监控调度组件采用独裁模式,同时给决策者做集群来规避单点。
高可用小结
- 核心手段冗余,计算高可用相对简单;存储高可用最难,重点解决数据不一致。
- 状态决策没有完美方案,每种模式都有缺陷,业务场景做取舍。
- 业界指标:99.99%(4 个 9),一年最多允许 52.56 分钟不可用。
三、复杂度来源 3:可扩展性
可扩展性定义:系统应对未来需求变化的能力。新增需求时尽量少修改旧代码,不需要整体重构系统 。面向对象、设计模式最初诞生,就是为了解决可扩展问题。
设计好可扩展性有两个前提:正确预测变化、完美封装变化 ,两件事本身都充满复杂度。
3.1 预测变化的困境
- 不能什么地方都预留扩展:每个点都做抽象预留未来,带来严重过度设计,项目庞大无法落地。
- 又不能完全不考虑扩展:需求一变就要推倒重构,成本爆炸。
- 预测本身会出错:预估的需求永远不来,投入大量开发做的扩展完全用不上。
实战建议:不要为想象中的未来做过度设计;只有变化概率高的点做扩展,没有发生的变化,等到真正来了再重构。
3.2 应对变化的两种架构手段
方案一:分离「变化层」和「稳定层」
把系统中经常变动的逻辑放到变化层;把核心不变逻辑放到稳定层。两种形式:
多种接入协议(XML/JSON/Protobuf)是变化层,底层业务逻辑稳定层。

多种数据库实现(MySQL/Oracle/DB2)是变化层,上层业务逻辑稳定层。

复杂度来源:
- 划分哪部分是变化、哪部分稳定,团队之间容易有分歧。
- 层之间接口设计难度极高 ,接口既要稳定,又要兼容多种变化层的差异。
方案二:抽象层 + 实现层
抽象层规则固定稳定;新增业务只新增实现,不改动抽象。典型代表:设计模式(装饰器、策略模式)、规则引擎。代价:抽象本身会增加代码复杂度,简单业务硬套模式,反而让代码难懂。
现代云原生案例:
- Dapr 就是抽象层,屏蔽消息队列、状态存储不同中间件差异,业务代码只调用 Dapr 抽象 API;
- SPI 机制,接口是抽象层,不同厂商实现是实现层。
可扩展性小结
可扩展性不等于 "全部预留未来能力",警惕过度设计 ;
核心思想:隔离变化,变化部分和稳定部分解耦;
开闭原则:对扩展开放,对修改关闭;但是落地很难,需要结合业务判断。
四、复杂度来源 4:低成本
高性能、高可用通常做法是增加服务器;低成本正好相反,希望减少机器,低成本往往是附加约束,不是第一目标 。
矛盾:想要更高性能更高可用,通常要花更多钱;想要省钱,就要在性能可用性上做取舍。
实现低成本主要依靠创新 ,分两种:
- 引入新技术(中小企业主流) :学习新技术,集成到现有系统。
案例:Redis、Elasticsearch、Kafka,解决传统数据库痛点,降低机器数量。
- 自研创造新技术(大公司) :投入人力从零造轮子,成本极高,只有大厂有资源。
案例:LinkedIn 开发 Kafka;Facebook HHVM;新浪微博 SSD 二级缓存架构。
现代云原生时代低成本手段:
- 使用云厂商托管服务,不用自己运维中间件,降低人力成本;
- 弹性伸缩:流量低峰缩掉机器,高峰扩容,减少闲置资源浪费;
- 语言替换:把 PHP/Python 热点服务改成 Go,相同 QPS 需要机器数大幅下降。
复杂度:新技术学习成本、新旧系统兼容、迁移风险。
五、复杂度来源 5:安全
安全分为两类:功能安全(防小偷)、架构安全(防强盗) 。
功能安全:防小偷
对应 XSS、CSRF、SQL 注入、密码漏洞。根源代码缺陷。特点:攻防对抗,无法一次性做到完美,框架只能防御已知漏洞,新漏洞会不断出现,持续迭代修复。
架构安全:防强盗
大规模恶意攻击,例如 DDoS 大流量打垮系统。传统企业依靠多层防火墙隔离网络区域。互联网业务特点:大并发,防火墙性能扛不住海量 DDoS 攻击。现在主流依靠云厂商 / 运营商流量清洗,自己很少硬扛大攻击。
现代云原生安全:零信任、网络策略 NetworkPolicy、WAF、容器镜像安全扫描,属于架构安全;代码 SAST 扫描属于功能安全。
六、复杂度来源 6:规模(量变引发质变)
系统功能数量、数据量达到阈值,会发生量变引起质变 ,带来全新复杂度,哪怕没有高性能高可用需求,系统依然会很复杂。
功能规模膨胀
系统功能越来越多,功能之间互相调用关联,复杂度不是线性上涨,是组合爆炸。
举例:3 个功能,复杂度 = 3+3=6;8 个功能,复杂度 = 8+28=36。新增功能,不是只新增自己,而是新增和其他所有老功能的交互。现象:老系统,没人敢改,看不懂,改一处牵动全身。
数据规模膨胀
例如 MySQL 单表从百万上涨到十亿行:索引建立、DDL 变更、备份、查询性能全部出现问题,不得不做分库分表。分库分表又引出一堆新问题:分片规则、跨分片查询、join、分布式事务。
现代案例:大数据技术栈 Hadoop、ClickHouse 就是为了解决传统数据库扛不住的数据规模问题。
七、六大复杂度综合实战思考
- 没有系统会同时把 6 点做到极致,一定需要取舍 。
- 银行支付系统:优先高可用、安全;其次性能;可扩展、成本次之。
- 初创小业务:优先可扩展性,低成本;高性能高可用不需要过度设计。
- 大数据分析平台:优先规模、成本;可用性可以适当放宽。
- 常见误区:
- 一味堆机器解决一切:会带来成本和运维复杂度。
- 为扩展而过度拆分微服务:带来网络、一致性、链路复杂度。
- 只看功能需求,忽略六大非功能复杂度。
- 架构师核心工作:识别系统主要的复杂度来源,针对性设计方案,拒绝为不存在的问题提前买单 。
思考题(原文课后思考)
- 你的业务系统中,哪几个复杂度来源最突出?是否还有其他额外复杂度?
- 高性能与高可用相比,哪个更复杂,为什么?
- 你写过哪些可扩展的代码 / 架构,实际效果如何?
补充:现实还会衍生额外复杂度,比如团队人员水平、第三方外部系统集成、合规监管等,也会进一步放大整体系统复杂度。