# 并行与并发

作者: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) 系统能容忍网络分区或节点故障

# 网络的八大谬误

  1. 网络是可靠的
  2. 延迟可以忽略
  3. 带宽无限
  4. 网络安全
  5. 拓扑不变
  6. 只有一个管理员
  7. 传输无成本
  8. 网络是同构的

正因为这些假设都是错的,所以我们必须考虑分区容错性(P)


# CAP 的权衡与常见策略

舍弃项 示例 特点
舍弃 P 单点数据库 不具分区容错性,但简单可靠
舍弃 A ZooKeeper 保证强一致性,牺牲部分可用性
舍弃 C Eureka 保证高可用与容错,牺牲强一致性(最终一致)

大部分互联网场景都属于 AP 系统:舍弃强一致性,保证最终一致。


# 五、服务内的并发(内部并发)

并发不仅体现在系统层面,也存在于服务内部。

# 1. 进程、线程、协程

概念 特点
进程 操作系统资源分配的最小单位
线程 CPU 调度的最小单位,共享进程内资源
协程 轻量级线程,在用户态切换,开销更低

Java 官方尚未正式推出原生协程(Project Loom 仍在发展中)。
类似库:Quasar、Kotlin 协程等。


# 2. 多线程的两大目的

  1. 提升效率
    当 I/O 操作阻塞时,CPU 可并行计算其他任务。
  2. 实现异步
    主线程可提前释放,后台任务(如发邮件、记录日志、第三方上报)异步执行,降低平均响应时间。

# 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 理论指导分布式架构的取舍;
  • 一切优化都应以压测与实际瓶颈分析为依据。

架构没有完美,只有权衡。能稳定支撑业务增长的系统,才是真正的好架构。