# 并行与并发
作者:Ethan.Yang
博客:https://blog.ethanyang.cn (opens new window)
上一篇文章介绍了负载均衡的系统设计,本篇将深入讲解**并行(Parallelism)与并发(Concurrency)**的概念、区别与系统设计思路。
# 一、并行与并发的区别
在系统设计中,我们常提到“并发”“并行”,但两者含义并不相同:
- 并发(Concurrency):关注同时处理多个任务的能力。
一个核心思想是“交替执行”——任务看似同时发生,但实际上是通过调度在同一时间片内交替执行。 - 并行(Parallelism):关注真正的同时执行。
当系统拥有多个处理单元(如多核CPU),多个任务可以在同一时刻被同时处理。
举例:
- 并发:一个人炒菜 + 打电话(交替进行)。
- 并行:两个人分别炒菜和打电话(同时进行)。
在服务架构中,我们通常说的“高并发”,指的其实是系统同时响应大量请求的能力,而非必须“并行执行”。
# 二、服务层面的并发设计
# 1. 集群(Cluster)
集群是由多个“同质化”的服务节点组成的整体:
- 每个节点运行相同的程序;
- 对外提供相同的接口服务;
- 一般共享同一存储(数据库、缓存等)。
# 优点
- 提升系统整体吞吐能力;
- 支持负载均衡;
- 便于水平扩展。
# 注意
- 若服务节点有状态(状态保存在内存),则会带来数据一致性和同步问题;
- 无状态服务更易扩展,可通过动态扩缩容实现弹性。
小建议:内存数据(如短信模板、配置Map)若量小,可直接在内存中维护;
若量大,应使用共享存储(Redis、DB)来保持一致。
# 2. 集群类型与对比
| 类型 | 特点 | 优点 | 缺点 |
|---|---|---|---|
| 单节点集群 | 单服务节点运行 | 架构简单 | 无容错、易丢数据 |
| 信息共享集群 | 多节点共享存储 | 提升计算能力 | 存储成为瓶颈 |
| 信息一致集群 | 节点数据强一致 | 可靠性高 | 实现复杂,成本高 |
例如阿里“盘古”、谷歌的分布式存储系统等,都是信息一致性集群的典型代表。
# 3. 一致性分类
根据系统在变更传播中的延迟程度,可分为:
- 强一致性(Strong Consistency):写入后立刻可读;
- 弱一致性(Weak Consistency):允许短暂读取旧数据;
- 最终一致性(Eventual Consistency):经过一段时间后数据最终一致。
工程上不要追求“完美一致”,要根据业务场景权衡。
# 三、分布式系统与微服务架构
# 1. 分布式系统的动机
当单体系统无法支撑高并发时,我们通常会:
- 拆应用(垂直拆分):将功能模块分开部署;
- 做集群(水平扩展):增加同类节点提升容量。
例如:
用户下订单 → 增加积分。
若出现新需求“下订单但不增加积分”,即可通过模块解耦、服务化来实现灵活扩展。
# 2. 微服务系统
微服务进一步将系统拆解为独立的业务单元:
- 各服务独立部署(如订单服务、积分服务);
- 保证接口协议不变,内部实现可随时替换;
- 支持独立扩缩容、独立容错。
总结:分布式解决“规模问题”,微服务解决“组织和演化问题”。
# 四、CAP 理论:分布式的基础
CAP 是分布式系统的理论基石:
| 特性 | 含义 |
|---|---|
| C - 一致性(Consistency) | 数据读写一致 |
| A - 可用性(Availability) | 服务始终可响应 |
| P - 分区容错性(Partition Tolerance) | 系统能容忍网络分区或节点故障 |
# 网络的八大谬误
- 网络是可靠的
- 延迟可以忽略
- 带宽无限
- 网络安全
- 拓扑不变
- 只有一个管理员
- 传输无成本
- 网络是同构的
正因为这些假设都是错的,所以我们必须考虑分区容错性(P)。
# CAP 的权衡与常见策略
| 舍弃项 | 示例 | 特点 |
|---|---|---|
| 舍弃 P | 单点数据库 | 不具分区容错性,但简单可靠 |
| 舍弃 A | ZooKeeper | 保证强一致性,牺牲部分可用性 |
| 舍弃 C | Eureka | 保证高可用与容错,牺牲强一致性(最终一致) |
大部分互联网场景都属于 AP 系统:舍弃强一致性,保证最终一致。
# 五、服务内的并发(内部并发)
并发不仅体现在系统层面,也存在于服务内部。
# 1. 进程、线程、协程
| 概念 | 特点 |
|---|---|
| 进程 | 操作系统资源分配的最小单位 |
| 线程 | CPU 调度的最小单位,共享进程内资源 |
| 协程 | 轻量级线程,在用户态切换,开销更低 |
Java 官方尚未正式推出原生协程(Project Loom 仍在发展中)。
类似库:Quasar、Kotlin 协程等。
# 2. 多线程的两大目的
- 提升效率:
当 I/O 操作阻塞时,CPU 可并行计算其他任务。 - 实现异步:
主线程可提前释放,后台任务(如发邮件、记录日志、第三方上报)异步执行,降低平均响应时间。
# 3. 线程数的估算
常用经验公式:
线程数 = CPU核数 × CPU使用率 × (1 + W/C)
其中 W = 等待时间,C = 计算时间。
或:
线程数 = CPU核数 × (1 - 阻塞系数)
阻塞系数 = W / (W + C)
# 4. 实战经验
- CPU 密集型任务:线程数 ≈ CPU 核数 + 1
- IO 密集型任务:线程数 ≈ CPU 核数 × 2 或更多
- 实际工程中:以 压测结果为准,经验值通常为
2 × CPU核数。
# 5. 系统负载监控
通过 top 命令查看:
load average(1min / 5min / 15min)- 当系统平均负载 > 0.7 × CPU 核数,应开始排查;
- 当 > 1.0 × CPU 核数,应立即优化;
- 当 > 5 × CPU 核数,说明系统已严重过载。
# 六、总结
- 并发 ≠ 并行:前者是任务调度,后者是任务同时执行;
- 服务层的并发通过集群、分布式、微服务实现;
- 服务内的并发通过线程、协程、异步机制实现;
- CAP 理论指导分布式架构的取舍;
- 一切优化都应以压测与实际瓶颈分析为依据。
架构没有完美,只有权衡。能稳定支撑业务增长的系统,才是真正的好架构。