架构设计:单机到分布式演进、无状态与有状态服务设计、数据一致性考量

推理服务的高可用架构并非一蹴而就,而是随着业务规模、延迟要求与成本约束逐步演进的。理解从单机到分布式的路径,掌握无状态与有状态服务的本质差异,并能在数据一致性上做出合理取舍,是构建生产级推理系统的核心能力。

一、核心原理:从单机瓶颈到分布式扩展

单机推理服务通常以 GPU 或 CPU 为计算核心,配合本地模型权重与缓存。其优势在于部署简单、无网络开销、数据天然一致;但受限于单卡显存、算力上限与故障域,无法支撑高并发或大模型推理。分布式演进的核心动力来自三个维度:吞吐量(QPS)、模型规模(超出单卡显存)与可用性(单点故障不可接受)。

演进路径的关键判断:

  • 垂直扩展:更换更强 GPU/CPU、增加显存,适合 QPS 需求 < 1000 且模型可完整放入单卡。
  • 水平扩展(副本):多实例负载均衡,适合无状态推理服务,是性价比最高的第一步。
  • 模型并行:张量并行 / 流水线并行,解决单卡放不下的大模型(如 70B+ 参数)。
  • 混合部署:将预填充(prefill)与解码(decode)分离,或按请求优先级路由到不同规格实例。

下图展示了从单机到分布式多阶段演进中,服务架构与关键组件的对应关系:

单机阶段 水平扩展阶段 分布式并行阶段 高级阶段 GPU 服务器 模型 + 推理引擎 本地缓存 KV Cache 客户端直连 无负载均衡 ✗ 单点故障 ✗ 显存上限 ✗ QPS 受限 扩容 负载均衡 Nginx / LB 实例 A 无状态 实例 B 无状态 实例 C 无状态 共享模型存储 S3 / NFS / OSS 模型并行 分布式调度器 Ray / K8s + 自定义 TP 组 GPU0 TP 组 GPU1 PP 组 GPU2 PP 组 GPU3 NVLink / RDMA 高速互联 服务化 Prefill 池 Decode 池 路由策略 弹性伸缩 演进核心:无状态水平扩展 → 有状态并行拆分 → 基于负载特征的精细化调度

二、无状态与有状态服务设计

无状态服务是指每个请求独立处理,服务实例不保存与特定用户或会话相关的数据。推理服务中,只要模型权重从共享存储加载且不维护对话历史,即可设计为无状态。其最大优势是任意实例可替换、可弹性伸缩,负载均衡器可以自由分发请求。

有状态服务则需要在实例间共享或持久化数据。推理场景中最典型的是多轮对话的 KV Cache、增量微调后的模型权重、请求级状态机(如流式生成中的游标)。有状态设计通常引入外部存储(Redis、数据库)或采用一致性哈希将同一会话固定路由到同一实例。

维度 无状态推理服务 有状态推理服务
会话保持 不需要,每次请求独立 需要,如多轮对话上下文
水平扩展 直接加实例,无额外成本 需迁移或复制状态,复杂
故障恢复 秒级替换,无数据丢失 需从外部存储恢复或重建
典型实现 模型只读 + 请求不带上下文 KV Cache 外置 / 会话粘滞
适用场景 单轮问答、批量推理、embedding 多轮对话、代码补全、流式生成

设计建议:优先将服务设计为无状态。若必须支持多轮对话,可将 KV Cache 或对话历史存入 Redis 等外部组件,让计算实例保持无状态;仅在外部存储延迟不可接受时,才考虑会话粘滞路由。

三、数据一致性考量

分布式推理中的数据一致性主要涉及三类:模型版本一致性、会话状态一致性、缓存一致性。

3.1 模型版本一致性

当模型从 v1 升级到 v2 时,若部分实例仍加载 v1,部分已加载 v2,会导致同一请求在不同时间得到不同结果。解决方案包括:

  • 蓝绿部署:先启动全部 v2 实例,验证通过后再切换流量。
  • 版本号路由:请求携带期望版本,网关按版本转发。
  • 原子发布:通过服务注册中心确保只有全部实例就绪后才对外提供服务。

3.2 会话状态一致性

多轮对话中,若用户请求被路由到不同实例,且实例各自维护上下文,则对话会中断。常用方案:

# 方案一:外部化存储(推荐)
对话历史 → Redis(key: session_id)
每次请求携带 session_id,服务端从 Redis 加载上下文
推理完成后更新 Redis 中的 KV Cache

# 方案二:一致性哈希路由
hash(session_id) % N → 固定实例
仅当实例扩缩容时才会发生重新映射

3.3 缓存一致性

推理结果缓存(如 prompt 前缀缓存)在分布式环境下需要失效机制。若模型更新,旧缓存必须同步失效,否则会返回过期结果。可采用 Redis 缓存 + 版本号作为缓存 key 的一部分:

cache_key = f"infer:{model_version}:{hash(prompt)}"
# 模型升级后,model_version 变化,自动使用新缓存空间

四、案例与场景复盘

案例:某智能客服平台从单机到分布式的演进

  1. 初期(单机):1 台 A10 GPU 服务器部署 7B 模型,QPS 约 50,单轮问答为主。故障时服务完全不可用。
  2. 中期(水平扩展):增加到 4 台无状态实例,前置 Nginx 负载均衡。模型权重存放在共享 NAS,启动时加载。QPS 提升至 200,单实例故障不影响整体。
  3. 后期(有状态拆分):业务增加多轮对话功能,将对话历史存入 Redis,计算实例保持无状态。同时引入前缀缓存,将相同系统提示词的 prefill 结果缓存,减少重复计算。
  4. 当前(分布式并行):模型升级到 70B,单卡放不下,采用 4 卡张量并行 + 2 卡流水线并行部署。引入 Ray 作为分布式调度器,根据请求长度动态路由到 prefill 专用实例或 decode 专用实例。

常见误区与复盘:

  • 误区一:认为所有推理服务都必须无状态。实际上,若多轮对话延迟敏感且外部存储开销过大,合理的会话粘滞反而能降低 p99 延迟。
  • 误区二:忽略模型版本一致性。灰度发布时未隔离流量,导致同一用户多次请求结果不一致,引发投诉。
  • 误区三:缓存设计未考虑版本失效。模型升级后仍命中旧缓存,返回过时答案。复盘后改为缓存 key 中携带模型版本号。
  • 误区四:分布式并行时未考虑通信开销。张量并行每层都需要 AllReduce,跨机通信延迟可能抵消算力收益。应优先在同一节点内使用 NVLink 进行张量并行。

架构设计的本质是在性能、成本、复杂度与一致性之间寻找平衡点。无状态服务是默认首选,但必须正视有状态需求的存在;数据一致性不是非黑即白,而是根据业务容忍度选择最终一致、会话级一致或强一致。每一次架构演进都应基于可量化的瓶颈分析,而非盲目追求技术先进性。