ray深度研究报告

Ray 深度研究报告

一份关于 Ray 分布式计算框架的架构梳理与工程取舍分析。内容覆盖编程模型、系统分层、调度与内存机制、容错设计,以及围绕 Ray Core 构建的上层生态。文中示意图使用 Mermaid 绘制(架构图、数据流图、调度决策图、容错流程图、ER 关系图等),可直接在支持 Mermaid 的 Markdown 渲染器中查看。


1. 为什么需要 Ray

2017 年,UC Berkeley 的 RISELab 发表了一篇后来影响深远的论文 Ray: A Distributed Framework for Emerging AI Applications(OSDI 2018)。论文的背景很具体:强化学习、超参数搜索、在线推理这类"新兴 AI 应用",对计算框架提出了既有系统都难以同时满足的要求。

当时主流的分布式框架各有硬伤:

  • Apache Spark 擅长批处理,但任务图(DAG)在提交时就已经固定,属于静态图;它面向无状态转换,缺少原生的有状态计算单元,调度延迟在秒级,无法支撑强化学习里那种"每秒动态生成上百万细粒度任务"的负载。
  • MPI 能做低延迟通信,但编程模型太底层,没有容错,没有动态任务图,节点挂了整个作业就完了。
  • 参数服务器 在分布式训练上很成熟,但它是为单一范式定制的,没法通用地表达"任务并行 + 有状态计算"的混合负载。

这些应用的共同特征是:任务图在运行时才动态构建,既有大量无状态的细粒度并行(仿真 rollout),又有大量需要保持状态的计算(策略网络、环境)。Ray 的核心主张,就是在同一套动态任务图里统一"任务并行"和"actor"两种抽象,再用一套能水平扩展、毫秒级调度的底层架构去支撑它。

这个项目后来催生了公司 Anyscale(由 Ion Stoica 等人创立),Ray 也从一个研究原型演进为生产级系统。理解 Ray,最好从它的编程模型入手,因为底层架构本质上是为了把这套模型跑得又快又稳。


2. 编程模型:Task、Actor、Object

Ray 的 API 表面极简,只有三个核心概念。

2.1 Task(任务)

@ray.remote 装饰的普通函数是 Task。它无状态、可被任意节点执行,调用后立刻返回一个 ObjectRef(类似 future),真正的计算结果异步产生。

python 复制代码
import ray
ray.init()

@ray.remote
def add(a, b):
    return a + b

ref = add.remote(1, 2)   # 立即返回 ObjectRef,不阻塞
result = ray.get(ref)    # 阻塞直到拿到结果:3

Task 的生命周期是"发射后不管":没有执行顺序保证,靠 Raylet 的调度决策来背压,失败则按谱系(lineage)重算。这种设计换来了极低的提交开销。

2.2 Actor(角色)

@ray.remote 装饰一个类,得到的就是 Actor。它是有状态的------类的实例常驻在某节点上,方法调用按顺序执行,状态在多次调用之间保留。这弥补了 Task 无状态的短板,让你可以把模型权重、环境、计数器等"放"在分布式内存里。

python 复制代码
@ray.remote
class Counter:
    def __init__(self):
        self.n = 0
    def inc(self):
        self.n += 1
        return self.n

c = Counter.remote()
c.inc.remote()   # 1
c.inc.remote()   # 2

Actor 的方法调用是有序且 exactly-once 的;更关键的是,Actor 之间的方法调用可以绕过 Raylet 直接通信,省掉一层调度开销。

2.3 Object 与 ObjectRef(对象与引用)

Task 和 Actor 方法返回的都是 ObjectRef,指向一个不可变对象。对象可以被直接作为另一个 Task 的参数传入,于是依赖关系自然编织成一张数据流图------Ray 在运行时构建出这张动态图,而不是像 Spark 那样在提交前就定死。

python 复制代码
x_ref = make_x.remote()
y_ref = make_y.remote()
z_ref = combine.remote(x_ref, y_ref)   # 依赖自动成为边

把上面三行展开成图,combinemake_xmake_y 输出的依赖,就是运行时由 ObjectRef 自动连上的边------你从不需要显式声明"谁等谁":
#mermaid-svg-fhqWmkgklfsVgBJl{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-fhqWmkgklfsVgBJl .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-fhqWmkgklfsVgBJl .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-fhqWmkgklfsVgBJl .error-icon{fill:#552222;}#mermaid-svg-fhqWmkgklfsVgBJl .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-fhqWmkgklfsVgBJl .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-fhqWmkgklfsVgBJl .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-fhqWmkgklfsVgBJl .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-fhqWmkgklfsVgBJl .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-fhqWmkgklfsVgBJl .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-fhqWmkgklfsVgBJl .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-fhqWmkgklfsVgBJl .marker{fill:#333333;stroke:#333333;}#mermaid-svg-fhqWmkgklfsVgBJl .marker.cross{stroke:#333333;}#mermaid-svg-fhqWmkgklfsVgBJl svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-fhqWmkgklfsVgBJl p{margin:0;}#mermaid-svg-fhqWmkgklfsVgBJl .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-fhqWmkgklfsVgBJl .cluster-label text{fill:#333;}#mermaid-svg-fhqWmkgklfsVgBJl .cluster-label span{color:#333;}#mermaid-svg-fhqWmkgklfsVgBJl .cluster-label span p{background-color:transparent;}#mermaid-svg-fhqWmkgklfsVgBJl .label text,#mermaid-svg-fhqWmkgklfsVgBJl span{fill:#333;color:#333;}#mermaid-svg-fhqWmkgklfsVgBJl .node rect,#mermaid-svg-fhqWmkgklfsVgBJl .node circle,#mermaid-svg-fhqWmkgklfsVgBJl .node ellipse,#mermaid-svg-fhqWmkgklfsVgBJl .node polygon,#mermaid-svg-fhqWmkgklfsVgBJl .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-fhqWmkgklfsVgBJl .rough-node .label text,#mermaid-svg-fhqWmkgklfsVgBJl .node .label text,#mermaid-svg-fhqWmkgklfsVgBJl .image-shape .label,#mermaid-svg-fhqWmkgklfsVgBJl .icon-shape .label{text-anchor:middle;}#mermaid-svg-fhqWmkgklfsVgBJl .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-fhqWmkgklfsVgBJl .rough-node .label,#mermaid-svg-fhqWmkgklfsVgBJl .node .label,#mermaid-svg-fhqWmkgklfsVgBJl .image-shape .label,#mermaid-svg-fhqWmkgklfsVgBJl .icon-shape .label{text-align:center;}#mermaid-svg-fhqWmkgklfsVgBJl .node.clickable{cursor:pointer;}#mermaid-svg-fhqWmkgklfsVgBJl .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-fhqWmkgklfsVgBJl .arrowheadPath{fill:#333333;}#mermaid-svg-fhqWmkgklfsVgBJl .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-fhqWmkgklfsVgBJl .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-fhqWmkgklfsVgBJl .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-fhqWmkgklfsVgBJl .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-fhqWmkgklfsVgBJl .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-fhqWmkgklfsVgBJl .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-fhqWmkgklfsVgBJl .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-fhqWmkgklfsVgBJl .cluster text{fill:#333;}#mermaid-svg-fhqWmkgklfsVgBJl .cluster span{color:#333;}#mermaid-svg-fhqWmkgklfsVgBJl div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-fhqWmkgklfsVgBJl .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-fhqWmkgklfsVgBJl rect.text{fill:none;stroke-width:0;}#mermaid-svg-fhqWmkgklfsVgBJl .icon-shape,#mermaid-svg-fhqWmkgklfsVgBJl .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-fhqWmkgklfsVgBJl .icon-shape p,#mermaid-svg-fhqWmkgklfsVgBJl .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-fhqWmkgklfsVgBJl .icon-shape .label rect,#mermaid-svg-fhqWmkgklfsVgBJl .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-fhqWmkgklfsVgBJl .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-fhqWmkgklfsVgBJl .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-fhqWmkgklfsVgBJl :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} x_ref 作为实参
y_ref 作为实参
Task:make_x

返回 x_ref
Task:make_y

返回 y_ref
Task:combine

(x_ref, y_ref)→ z_ref

这三个概念的组合,就是 Ray 表达一切上层库(训练、调参、服务)的"汇编语言"。


3. 系统架构:四层分工

Ray 的运行时由四类组件构成,职责切分得很干净。下面的架构图展示了一个典型集群:一个 Head 节点,加若干 Worker 节点。
#mermaid-svg-f0YtLZbVUS00mfef{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-f0YtLZbVUS00mfef .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-f0YtLZbVUS00mfef .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-f0YtLZbVUS00mfef .error-icon{fill:#552222;}#mermaid-svg-f0YtLZbVUS00mfef .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-f0YtLZbVUS00mfef .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-f0YtLZbVUS00mfef .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-f0YtLZbVUS00mfef .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-f0YtLZbVUS00mfef .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-f0YtLZbVUS00mfef .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-f0YtLZbVUS00mfef .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-f0YtLZbVUS00mfef .marker{fill:#333333;stroke:#333333;}#mermaid-svg-f0YtLZbVUS00mfef .marker.cross{stroke:#333333;}#mermaid-svg-f0YtLZbVUS00mfef svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-f0YtLZbVUS00mfef p{margin:0;}#mermaid-svg-f0YtLZbVUS00mfef .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-f0YtLZbVUS00mfef .cluster-label text{fill:#333;}#mermaid-svg-f0YtLZbVUS00mfef .cluster-label span{color:#333;}#mermaid-svg-f0YtLZbVUS00mfef .cluster-label span p{background-color:transparent;}#mermaid-svg-f0YtLZbVUS00mfef .label text,#mermaid-svg-f0YtLZbVUS00mfef span{fill:#333;color:#333;}#mermaid-svg-f0YtLZbVUS00mfef .node rect,#mermaid-svg-f0YtLZbVUS00mfef .node circle,#mermaid-svg-f0YtLZbVUS00mfef .node ellipse,#mermaid-svg-f0YtLZbVUS00mfef .node polygon,#mermaid-svg-f0YtLZbVUS00mfef .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-f0YtLZbVUS00mfef .rough-node .label text,#mermaid-svg-f0YtLZbVUS00mfef .node .label text,#mermaid-svg-f0YtLZbVUS00mfef .image-shape .label,#mermaid-svg-f0YtLZbVUS00mfef .icon-shape .label{text-anchor:middle;}#mermaid-svg-f0YtLZbVUS00mfef .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-f0YtLZbVUS00mfef .rough-node .label,#mermaid-svg-f0YtLZbVUS00mfef .node .label,#mermaid-svg-f0YtLZbVUS00mfef .image-shape .label,#mermaid-svg-f0YtLZbVUS00mfef .icon-shape .label{text-align:center;}#mermaid-svg-f0YtLZbVUS00mfef .node.clickable{cursor:pointer;}#mermaid-svg-f0YtLZbVUS00mfef .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-f0YtLZbVUS00mfef .arrowheadPath{fill:#333333;}#mermaid-svg-f0YtLZbVUS00mfef .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-f0YtLZbVUS00mfef .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-f0YtLZbVUS00mfef .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-f0YtLZbVUS00mfef .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-f0YtLZbVUS00mfef .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-f0YtLZbVUS00mfef .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-f0YtLZbVUS00mfef .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-f0YtLZbVUS00mfef .cluster text{fill:#333;}#mermaid-svg-f0YtLZbVUS00mfef .cluster span{color:#333;}#mermaid-svg-f0YtLZbVUS00mfef div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-f0YtLZbVUS00mfef .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-f0YtLZbVUS00mfef rect.text{fill:none;stroke-width:0;}#mermaid-svg-f0YtLZbVUS00mfef .icon-shape,#mermaid-svg-f0YtLZbVUS00mfef .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-f0YtLZbVUS00mfef .icon-shape p,#mermaid-svg-f0YtLZbVUS00mfef .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-f0YtLZbVUS00mfef .icon-shape .label rect,#mermaid-svg-f0YtLZbVUS00mfef .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-f0YtLZbVUS00mfef .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-f0YtLZbVUS00mfef .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-f0YtLZbVUS00mfef :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Worker 节点 N
Worker 节点 1
Head 节点
元数据 / 心跳同步
元数据 / 心跳同步
Driver

(运行用户程序的进程)
GCS

Global Control Service

集群元数据 / 控制面
Head Raylet

Scheduler + Object Store
Raylet

(本地调度 + 资源管理)
Object Store(Plasma 共享内存)
Worker 进程

执行 Task / 承载 Actor
Raylet
Object Store(Plasma)
Worker 进程

3.1 GCS(Global Control Service)

GCS 是 Ray 的"控制面大脑",本质是一个带发布/订阅能力的键值存储(早期基于 Redis,后来演进为独立的 GCS 服务进程)。它保存的是集群的控制状态,而非用户数据:

  • 任务表(哪些 Task 被提交、状态如何)
  • 对象表(每个对象存在哪个节点)
  • 函数表 / Actor 表(可执行的代码与实例)
  • 节点与 Worker 的心跳、资源占用
  • Placement Group(放置组,用于控制分配局部性)

GCS 最关键的设计决策是:把控制状态的"存储"和调度"逻辑"解耦。存储(GCS)可以独立水平扩展和容错(分片 + 复制),而调度器只是读取这些状态做决策。这点和 GFS、Omega 等把控制面耦合在一起的系统形成对比,也是 Ray 能同时做到高吞吐和毫秒级调度的前提。

3.2 Raylet(每节点守护进程)

每个节点上跑一个 Raylet,是真正的"本地管家"。它包含两个职能:

  1. Node Manager:管理本节点资源(CPU/GPU/内存/自定义资源)、维护 Worker 进程池、处理对象存储的钉住(pin)/溢写(spill)/淘汰、管理 Worker 租约(lease)。
  2. Scheduler(本地调度器):决定 Task 在本地如何排队、交给哪个空闲 Worker,以及在资源不足或需要远程放置时把任务"回溢"(spillback)给全局调度器。

3.3 Object Store(Plasma 共享内存)

每个节点有一个基于共享内存的对象存储 Plasma。同一个节点内的多个 Worker 进程通过内存映射零拷贝地读取同一份对象,避免了进程间序列化拷贝。跨节点时,由 Object Manager 负责按需在节点间搬运对象。论文实测单客户端写吞吐在对象较大时可超过 15 GB/s。

3.4 Worker 进程

真正干活的单元,无状态、按需从进程池里租借。它既执行 Task,也承载 Actor 的实例。


4. 调度机制:自底向上的分布式调度

Ray 调度要解决的是经典矛盾:既想要集中式调度器的全局视野(负载均衡、数据局部性),又想要去中心化调度器的低延迟和高吞吐。它的答案是自底向上(bottom-up)的拉取式调度

核心思想:Worker 不是"把任务丢给调度器去跑",而是向本地 Raylet 申请一个 Worker 租约(同时立刻拿到返回值的 ObjectRef ID)。本地 Raylet 优先在本地满足这个请求------因为参数对象往往就在本地共享内存里,零拷贝就能拿到。只有当本地放不下(缺资源、缺数据)时,才把任务回溢给全局调度器。

全局调度器拿到回溢的任务后,会查 GCS 里的对象表,找到参数所在节点,优先把任务调度到已经持有数据的节点(数据局部性),同时参考各节点负载做均衡。

下面这张时序图走一遍端到端流程:
Object Store Worker GCS 全局调度器 本地 Raylet(Scheduler) Driver Object Store Worker GCS 全局调度器 本地 Raylet(Scheduler) Driver #mermaid-svg-wnYyrpH4Spax9eKe{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-wnYyrpH4Spax9eKe .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-wnYyrpH4Spax9eKe .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-wnYyrpH4Spax9eKe .error-icon{fill:#552222;}#mermaid-svg-wnYyrpH4Spax9eKe .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-wnYyrpH4Spax9eKe .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-wnYyrpH4Spax9eKe .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-wnYyrpH4Spax9eKe .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-wnYyrpH4Spax9eKe .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-wnYyrpH4Spax9eKe .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-wnYyrpH4Spax9eKe .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-wnYyrpH4Spax9eKe .marker{fill:#333333;stroke:#333333;}#mermaid-svg-wnYyrpH4Spax9eKe .marker.cross{stroke:#333333;}#mermaid-svg-wnYyrpH4Spax9eKe svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-wnYyrpH4Spax9eKe p{margin:0;}#mermaid-svg-wnYyrpH4Spax9eKe .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-wnYyrpH4Spax9eKe text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-wnYyrpH4Spax9eKe .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-wnYyrpH4Spax9eKe .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-wnYyrpH4Spax9eKe .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-wnYyrpH4Spax9eKe .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-wnYyrpH4Spax9eKe #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-wnYyrpH4Spax9eKe .sequenceNumber{fill:white;}#mermaid-svg-wnYyrpH4Spax9eKe #sequencenumber{fill:#333;}#mermaid-svg-wnYyrpH4Spax9eKe #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-wnYyrpH4Spax9eKe .messageText{fill:#333;stroke:none;}#mermaid-svg-wnYyrpH4Spax9eKe .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-wnYyrpH4Spax9eKe .labelText,#mermaid-svg-wnYyrpH4Spax9eKe .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-wnYyrpH4Spax9eKe .loopText,#mermaid-svg-wnYyrpH4Spax9eKe .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-wnYyrpH4Spax9eKe .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-wnYyrpH4Spax9eKe .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-wnYyrpH4Spax9eKe .noteText,#mermaid-svg-wnYyrpH4Spax9eKe .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-wnYyrpH4Spax9eKe .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-wnYyrpH4Spax9eKe .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-wnYyrpH4Spax9eKe .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-wnYyrpH4Spax9eKe .actorPopupMenu{position:absolute;}#mermaid-svg-wnYyrpH4Spax9eKe .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-wnYyrpH4Spax9eKe .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-wnYyrpH4Spax9eKe .actor-man circle,#mermaid-svg-wnYyrpH4Spax9eKe line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-wnYyrpH4Spax9eKe :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} add.remote(a, b) 申请租约本地资源不足 / 数据不在本地回溢(spillback)查询参数 a、b 的位置返回对象位置(b 在节点 N2)调度到 N2(数据局部性)拉取依赖对象 a、b本地共享内存零拷贝 / 跨节点传输执行 add(a, b)写入结果 c注册对象 c 的位置ray.get(id_c)返回结果 c

而调度"要不要回溢、回溢后放哪"的分支判断,用一张决策图看得更清楚:
#mermaid-svg-lbv6prmzlVTizzvU{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-lbv6prmzlVTizzvU .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-lbv6prmzlVTizzvU .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-lbv6prmzlVTizzvU .error-icon{fill:#552222;}#mermaid-svg-lbv6prmzlVTizzvU .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-lbv6prmzlVTizzvU .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-lbv6prmzlVTizzvU .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-lbv6prmzlVTizzvU .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-lbv6prmzlVTizzvU .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-lbv6prmzlVTizzvU .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-lbv6prmzlVTizzvU .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-lbv6prmzlVTizzvU .marker{fill:#333333;stroke:#333333;}#mermaid-svg-lbv6prmzlVTizzvU .marker.cross{stroke:#333333;}#mermaid-svg-lbv6prmzlVTizzvU svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-lbv6prmzlVTizzvU p{margin:0;}#mermaid-svg-lbv6prmzlVTizzvU .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-lbv6prmzlVTizzvU .cluster-label text{fill:#333;}#mermaid-svg-lbv6prmzlVTizzvU .cluster-label span{color:#333;}#mermaid-svg-lbv6prmzlVTizzvU .cluster-label span p{background-color:transparent;}#mermaid-svg-lbv6prmzlVTizzvU .label text,#mermaid-svg-lbv6prmzlVTizzvU span{fill:#333;color:#333;}#mermaid-svg-lbv6prmzlVTizzvU .node rect,#mermaid-svg-lbv6prmzlVTizzvU .node circle,#mermaid-svg-lbv6prmzlVTizzvU .node ellipse,#mermaid-svg-lbv6prmzlVTizzvU .node polygon,#mermaid-svg-lbv6prmzlVTizzvU .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-lbv6prmzlVTizzvU .rough-node .label text,#mermaid-svg-lbv6prmzlVTizzvU .node .label text,#mermaid-svg-lbv6prmzlVTizzvU .image-shape .label,#mermaid-svg-lbv6prmzlVTizzvU .icon-shape .label{text-anchor:middle;}#mermaid-svg-lbv6prmzlVTizzvU .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-lbv6prmzlVTizzvU .rough-node .label,#mermaid-svg-lbv6prmzlVTizzvU .node .label,#mermaid-svg-lbv6prmzlVTizzvU .image-shape .label,#mermaid-svg-lbv6prmzlVTizzvU .icon-shape .label{text-align:center;}#mermaid-svg-lbv6prmzlVTizzvU .node.clickable{cursor:pointer;}#mermaid-svg-lbv6prmzlVTizzvU .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-lbv6prmzlVTizzvU .arrowheadPath{fill:#333333;}#mermaid-svg-lbv6prmzlVTizzvU .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-lbv6prmzlVTizzvU .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-lbv6prmzlVTizzvU .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-lbv6prmzlVTizzvU .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-lbv6prmzlVTizzvU .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-lbv6prmzlVTizzvU .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-lbv6prmzlVTizzvU .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-lbv6prmzlVTizzvU .cluster text{fill:#333;}#mermaid-svg-lbv6prmzlVTizzvU .cluster span{color:#333;}#mermaid-svg-lbv6prmzlVTizzvU div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-lbv6prmzlVTizzvU .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-lbv6prmzlVTizzvU rect.text{fill:none;stroke-width:0;}#mermaid-svg-lbv6prmzlVTizzvU .icon-shape,#mermaid-svg-lbv6prmzlVTizzvU .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-lbv6prmzlVTizzvU .icon-shape p,#mermaid-svg-lbv6prmzlVTizzvU .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-lbv6prmzlVTizzvU .icon-shape .label rect,#mermaid-svg-lbv6prmzlVTizzvU .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-lbv6prmzlVTizzvU .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-lbv6prmzlVTizzvU .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-lbv6prmzlVTizzvU :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是

数据局部性
负载均衡
Worker 申请 Task 租约
本地资源充足

且依赖对象已在本地?
本地 Raylet 直接调度

零拷贝取依赖
回溢(spillback)

给全局调度器
查 GCS 对象表:

参数落在哪个节点?
调度到持有数据的节点
调度到负载最低的节点
Worker 执行 Task

这套机制的特点可以归纳成几点:

  • 分布式:每个 Raylet 拿着集群视图独立决策,不依赖单一中心逐任务批准。
  • 自底向上:Worker 驱动,Raylet 响应,全局调度器只兜底。
  • 最终一致:靠周期性同步而非强一致协议,把常见情况做到极致快,异常时优雅退化。
  • 延迟优先于最优放置:它追求的是"够好的放置 + 极低延迟",而不是每次都算出全局最优。

这也带来一个明确的适用边界:当工作负载是海量小对象、极短生命周期、超大 fan-out 时,协议本身的协调开销会压过计算本身------这种情况下 Ray 并不合适。它的目标负载是"大块、长寿命、适度共享"的 ML 对象。


5. 对象存储与分布式垃圾回收

对象怎么存、怎么回收,是 Ray 内存管理的两道核心题。

存储层由三部分协作:Plasma 负责节点内共享内存;Object Manager 负责节点间传输和定位;Reference Manager 负责跟踪对象的归属与引用。全局还有一个对象目录,记录对象当前在哪个节点。

Worker 真正去读一个对象时,走的路径是这样的------先问本地 Plasma,没命中才跨节点拉:
#mermaid-svg-oBvbNO6uLRC6Uc0x{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-oBvbNO6uLRC6Uc0x .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-oBvbNO6uLRC6Uc0x .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-oBvbNO6uLRC6Uc0x .error-icon{fill:#552222;}#mermaid-svg-oBvbNO6uLRC6Uc0x .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-oBvbNO6uLRC6Uc0x .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-oBvbNO6uLRC6Uc0x .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-oBvbNO6uLRC6Uc0x .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-oBvbNO6uLRC6Uc0x .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-oBvbNO6uLRC6Uc0x .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-oBvbNO6uLRC6Uc0x .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-oBvbNO6uLRC6Uc0x .marker{fill:#333333;stroke:#333333;}#mermaid-svg-oBvbNO6uLRC6Uc0x .marker.cross{stroke:#333333;}#mermaid-svg-oBvbNO6uLRC6Uc0x svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-oBvbNO6uLRC6Uc0x p{margin:0;}#mermaid-svg-oBvbNO6uLRC6Uc0x .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-oBvbNO6uLRC6Uc0x .cluster-label text{fill:#333;}#mermaid-svg-oBvbNO6uLRC6Uc0x .cluster-label span{color:#333;}#mermaid-svg-oBvbNO6uLRC6Uc0x .cluster-label span p{background-color:transparent;}#mermaid-svg-oBvbNO6uLRC6Uc0x .label text,#mermaid-svg-oBvbNO6uLRC6Uc0x span{fill:#333;color:#333;}#mermaid-svg-oBvbNO6uLRC6Uc0x .node rect,#mermaid-svg-oBvbNO6uLRC6Uc0x .node circle,#mermaid-svg-oBvbNO6uLRC6Uc0x .node ellipse,#mermaid-svg-oBvbNO6uLRC6Uc0x .node polygon,#mermaid-svg-oBvbNO6uLRC6Uc0x .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-oBvbNO6uLRC6Uc0x .rough-node .label text,#mermaid-svg-oBvbNO6uLRC6Uc0x .node .label text,#mermaid-svg-oBvbNO6uLRC6Uc0x .image-shape .label,#mermaid-svg-oBvbNO6uLRC6Uc0x .icon-shape .label{text-anchor:middle;}#mermaid-svg-oBvbNO6uLRC6Uc0x .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-oBvbNO6uLRC6Uc0x .rough-node .label,#mermaid-svg-oBvbNO6uLRC6Uc0x .node .label,#mermaid-svg-oBvbNO6uLRC6Uc0x .image-shape .label,#mermaid-svg-oBvbNO6uLRC6Uc0x .icon-shape .label{text-align:center;}#mermaid-svg-oBvbNO6uLRC6Uc0x .node.clickable{cursor:pointer;}#mermaid-svg-oBvbNO6uLRC6Uc0x .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-oBvbNO6uLRC6Uc0x .arrowheadPath{fill:#333333;}#mermaid-svg-oBvbNO6uLRC6Uc0x .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-oBvbNO6uLRC6Uc0x .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-oBvbNO6uLRC6Uc0x .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-oBvbNO6uLRC6Uc0x .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-oBvbNO6uLRC6Uc0x .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-oBvbNO6uLRC6Uc0x .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-oBvbNO6uLRC6Uc0x .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-oBvbNO6uLRC6Uc0x .cluster text{fill:#333;}#mermaid-svg-oBvbNO6uLRC6Uc0x .cluster span{color:#333;}#mermaid-svg-oBvbNO6uLRC6Uc0x div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-oBvbNO6uLRC6Uc0x .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-oBvbNO6uLRC6Uc0x rect.text{fill:none;stroke-width:0;}#mermaid-svg-oBvbNO6uLRC6Uc0x .icon-shape,#mermaid-svg-oBvbNO6uLRC6Uc0x .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-oBvbNO6uLRC6Uc0x .icon-shape p,#mermaid-svg-oBvbNO6uLRC6Uc0x .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-oBvbNO6uLRC6Uc0x .icon-shape .label rect,#mermaid-svg-oBvbNO6uLRC6Uc0x .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-oBvbNO6uLRC6Uc0x .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-oBvbNO6uLRC6Uc0x .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-oBvbNO6uLRC6Uc0x :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是

Worker 需要对象 O
本地 Plasma 命中?
内存映射零拷贝读取
Object Manager 查 GCS

定位对象所在节点
跨节点拉取对象
写入本地 Object Store

(按需钉住 pin)

回收层用的是"所有权模型"(ownership model)的分布式引用计数:每个对象都有一个 owner(通常是创建它的那个 Worker),owner 跨节点维护该对象的引用计数。当引用计数归零,对象被释放。这套机制避免了中心化的 GC 瓶颈------没有哪个节点需要统揽全局所有对象的生死。
#mermaid-svg-Wiy1ZR3X6ANV4lfh{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Wiy1ZR3X6ANV4lfh .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Wiy1ZR3X6ANV4lfh .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Wiy1ZR3X6ANV4lfh .error-icon{fill:#552222;}#mermaid-svg-Wiy1ZR3X6ANV4lfh .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Wiy1ZR3X6ANV4lfh .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Wiy1ZR3X6ANV4lfh .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Wiy1ZR3X6ANV4lfh .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Wiy1ZR3X6ANV4lfh .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Wiy1ZR3X6ANV4lfh .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Wiy1ZR3X6ANV4lfh .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Wiy1ZR3X6ANV4lfh .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Wiy1ZR3X6ANV4lfh .marker.cross{stroke:#333333;}#mermaid-svg-Wiy1ZR3X6ANV4lfh svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Wiy1ZR3X6ANV4lfh p{margin:0;}#mermaid-svg-Wiy1ZR3X6ANV4lfh .entityBox{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-Wiy1ZR3X6ANV4lfh .relationshipLabelBox{fill:hsl(80, 100%, 96.2745098039%);opacity:0.7;background-color:hsl(80, 100%, 96.2745098039%);}#mermaid-svg-Wiy1ZR3X6ANV4lfh .relationshipLabelBox rect{opacity:0.5;}#mermaid-svg-Wiy1ZR3X6ANV4lfh .labelBkg{background-color:rgba(248.6666666666, 255, 235.9999999999, 0.5);}#mermaid-svg-Wiy1ZR3X6ANV4lfh .edgeLabel .label{fill:#9370DB;font-size:14px;}#mermaid-svg-Wiy1ZR3X6ANV4lfh .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Wiy1ZR3X6ANV4lfh .edge-pattern-dashed{stroke-dasharray:8,8;}#mermaid-svg-Wiy1ZR3X6ANV4lfh .node rect,#mermaid-svg-Wiy1ZR3X6ANV4lfh .node circle,#mermaid-svg-Wiy1ZR3X6ANV4lfh .node ellipse,#mermaid-svg-Wiy1ZR3X6ANV4lfh .node polygon{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Wiy1ZR3X6ANV4lfh .relationshipLine{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-Wiy1ZR3X6ANV4lfh .marker{fill:none!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-Wiy1ZR3X6ANV4lfh .edgeLabel{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Wiy1ZR3X6ANV4lfh .edgeLabel .label rect{fill:rgba(232,232,232, 0.8);}#mermaid-svg-Wiy1ZR3X6ANV4lfh .edgeLabel .label text{fill:#333;}#mermaid-svg-Wiy1ZR3X6ANV4lfh :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 包含
管理
承载
运行
拥有
调度
管理
执行
托管
产出
消费
产出
派生
派生
被引用
追踪
追踪
定位
绑定
CLUSTER
NODE
GCS
WORKER
RAYLET
OBJECTSTORE
TASK
ACTOR
OBJECT
JOB
OBJECTREF
PLACEMENTGROUP

上面的实体关系图刻画了 Ray 控制状态里几个关键概念之间的联系:集群由节点组成,节点上跑 Raylet、Object Store 和 Worker;Job 派生出 Task 和 Actor;Task/Actor 既消费对象也产出对象,对象通过 ObjectRef 被引用,其位置由 GCS 记录。

当 Object Store 内存吃紧时,Ray 会把对象溢写到磁盘(或 mmap 文件),并按策略淘汰。不过实践中对象存储 OOM 是最常见的一类静默故障 ------稳妥的做法是把每节点的 object_store_memory 限制在可用内存的 60% 以内。


6. 容错:基于谱系的重建

分布式系统里节点必挂,Ray 的容错思路按组件类型分别处理:

  • Task 失败:无状态,直接按谱系重算。因为 Task 的"配方"(函数 + 参数引用)被保留在 GCS 里,只要输入还在,就能重新执行一遍拿到结果,无需检查点。
  • Actor 失败 :有状态,重启由 GCS 按重启策略拉起新实例。注意:Actor 的内部状态默认不会自动恢复,需要用户自己做检查点(checkpoint)才能续上。
  • 对象丢失 :如果产出某对象的 Task 失败,而它的依赖还在,就靠谱系重建该对象。但如果对象的 owner(创建它的 Worker)挂了,这个对象就可能真的丢了------这是所有权模型在极端场景下的一个已知局限。
  • 节点失效:GCS 通过心跳探知,把上面的 Task / Actor 重新调度,并尝试重建对象。
  • GCS 自身:早期 GCS 是单点故障(SPOF),Head 节点一挂全集群瘫痪。现在 Ray 提供了 GCS 容错模式(可挂外部存储 / Redis,或启用 Ray 自带的 GCS HA);在 Kubernetes 上则交给 KubeRay 做节点与集群级恢复。

失败时 GCS 实际走的判定链路,用一张图串起来:
#mermaid-svg-s7bjNkGL9lvM6jKz{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-s7bjNkGL9lvM6jKz .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-s7bjNkGL9lvM6jKz .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-s7bjNkGL9lvM6jKz .error-icon{fill:#552222;}#mermaid-svg-s7bjNkGL9lvM6jKz .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-s7bjNkGL9lvM6jKz .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-s7bjNkGL9lvM6jKz .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-s7bjNkGL9lvM6jKz .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-s7bjNkGL9lvM6jKz .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-s7bjNkGL9lvM6jKz .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-s7bjNkGL9lvM6jKz .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-s7bjNkGL9lvM6jKz .marker{fill:#333333;stroke:#333333;}#mermaid-svg-s7bjNkGL9lvM6jKz .marker.cross{stroke:#333333;}#mermaid-svg-s7bjNkGL9lvM6jKz svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-s7bjNkGL9lvM6jKz p{margin:0;}#mermaid-svg-s7bjNkGL9lvM6jKz .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-s7bjNkGL9lvM6jKz .cluster-label text{fill:#333;}#mermaid-svg-s7bjNkGL9lvM6jKz .cluster-label span{color:#333;}#mermaid-svg-s7bjNkGL9lvM6jKz .cluster-label span p{background-color:transparent;}#mermaid-svg-s7bjNkGL9lvM6jKz .label text,#mermaid-svg-s7bjNkGL9lvM6jKz span{fill:#333;color:#333;}#mermaid-svg-s7bjNkGL9lvM6jKz .node rect,#mermaid-svg-s7bjNkGL9lvM6jKz .node circle,#mermaid-svg-s7bjNkGL9lvM6jKz .node ellipse,#mermaid-svg-s7bjNkGL9lvM6jKz .node polygon,#mermaid-svg-s7bjNkGL9lvM6jKz .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-s7bjNkGL9lvM6jKz .rough-node .label text,#mermaid-svg-s7bjNkGL9lvM6jKz .node .label text,#mermaid-svg-s7bjNkGL9lvM6jKz .image-shape .label,#mermaid-svg-s7bjNkGL9lvM6jKz .icon-shape .label{text-anchor:middle;}#mermaid-svg-s7bjNkGL9lvM6jKz .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-s7bjNkGL9lvM6jKz .rough-node .label,#mermaid-svg-s7bjNkGL9lvM6jKz .node .label,#mermaid-svg-s7bjNkGL9lvM6jKz .image-shape .label,#mermaid-svg-s7bjNkGL9lvM6jKz .icon-shape .label{text-align:center;}#mermaid-svg-s7bjNkGL9lvM6jKz .node.clickable{cursor:pointer;}#mermaid-svg-s7bjNkGL9lvM6jKz .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-s7bjNkGL9lvM6jKz .arrowheadPath{fill:#333333;}#mermaid-svg-s7bjNkGL9lvM6jKz .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-s7bjNkGL9lvM6jKz .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-s7bjNkGL9lvM6jKz .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-s7bjNkGL9lvM6jKz .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-s7bjNkGL9lvM6jKz .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-s7bjNkGL9lvM6jKz .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-s7bjNkGL9lvM6jKz .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-s7bjNkGL9lvM6jKz .cluster text{fill:#333;}#mermaid-svg-s7bjNkGL9lvM6jKz .cluster span{color:#333;}#mermaid-svg-s7bjNkGL9lvM6jKz div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-s7bjNkGL9lvM6jKz .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-s7bjNkGL9lvM6jKz rect.text{fill:none;stroke-width:0;}#mermaid-svg-s7bjNkGL9lvM6jKz .icon-shape,#mermaid-svg-s7bjNkGL9lvM6jKz .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-s7bjNkGL9lvM6jKz .icon-shape p,#mermaid-svg-s7bjNkGL9lvM6jKz .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-s7bjNkGL9lvM6jKz .icon-shape .label rect,#mermaid-svg-s7bjNkGL9lvM6jKz .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-s7bjNkGL9lvM6jKz .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-s7bjNkGL9lvM6jKz .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-s7bjNkGL9lvM6jKz :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 无状态 Task
Actor
对象

否 / owner 已死
节点 / Worker 失联
GCS 心跳超时

标记失效
失效对象类型?
按谱系重算

重新提交 Task 规格
GCS 按重启策略

拉起新实例
依赖是否仍在?
由产出 Task 重新计算
对象丢失(已知局限)
结果写回 Object Store

并刷新 GCS 位置
状态需用户检查点恢复

容错和性能之间始终有张力。Ray 选择轻量的谱系重建而非全面检查点,正是因为它面向的是"计算密集、对象大、重算成本相对可接受"的 ML 负载。


7. 上层生态:Ray Core 之上的库

Ray Core 提供了原语,真正让它在工业界落地的是架在 Core 上的一批库。它们共享同一套底层,所以你可以把数据加载(Data)、训练(Train)、调参(Tune)、服务(Serve)串成一条端到端的流水线。
#mermaid-svg-fEYA6Fr4AERY9282{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-fEYA6Fr4AERY9282 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-fEYA6Fr4AERY9282 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-fEYA6Fr4AERY9282 .error-icon{fill:#552222;}#mermaid-svg-fEYA6Fr4AERY9282 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-fEYA6Fr4AERY9282 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-fEYA6Fr4AERY9282 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-fEYA6Fr4AERY9282 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-fEYA6Fr4AERY9282 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-fEYA6Fr4AERY9282 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-fEYA6Fr4AERY9282 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-fEYA6Fr4AERY9282 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-fEYA6Fr4AERY9282 .marker.cross{stroke:#333333;}#mermaid-svg-fEYA6Fr4AERY9282 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-fEYA6Fr4AERY9282 p{margin:0;}#mermaid-svg-fEYA6Fr4AERY9282 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-fEYA6Fr4AERY9282 .cluster-label text{fill:#333;}#mermaid-svg-fEYA6Fr4AERY9282 .cluster-label span{color:#333;}#mermaid-svg-fEYA6Fr4AERY9282 .cluster-label span p{background-color:transparent;}#mermaid-svg-fEYA6Fr4AERY9282 .label text,#mermaid-svg-fEYA6Fr4AERY9282 span{fill:#333;color:#333;}#mermaid-svg-fEYA6Fr4AERY9282 .node rect,#mermaid-svg-fEYA6Fr4AERY9282 .node circle,#mermaid-svg-fEYA6Fr4AERY9282 .node ellipse,#mermaid-svg-fEYA6Fr4AERY9282 .node polygon,#mermaid-svg-fEYA6Fr4AERY9282 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-fEYA6Fr4AERY9282 .rough-node .label text,#mermaid-svg-fEYA6Fr4AERY9282 .node .label text,#mermaid-svg-fEYA6Fr4AERY9282 .image-shape .label,#mermaid-svg-fEYA6Fr4AERY9282 .icon-shape .label{text-anchor:middle;}#mermaid-svg-fEYA6Fr4AERY9282 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-fEYA6Fr4AERY9282 .rough-node .label,#mermaid-svg-fEYA6Fr4AERY9282 .node .label,#mermaid-svg-fEYA6Fr4AERY9282 .image-shape .label,#mermaid-svg-fEYA6Fr4AERY9282 .icon-shape .label{text-align:center;}#mermaid-svg-fEYA6Fr4AERY9282 .node.clickable{cursor:pointer;}#mermaid-svg-fEYA6Fr4AERY9282 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-fEYA6Fr4AERY9282 .arrowheadPath{fill:#333333;}#mermaid-svg-fEYA6Fr4AERY9282 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-fEYA6Fr4AERY9282 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-fEYA6Fr4AERY9282 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-fEYA6Fr4AERY9282 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-fEYA6Fr4AERY9282 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-fEYA6Fr4AERY9282 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-fEYA6Fr4AERY9282 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-fEYA6Fr4AERY9282 .cluster text{fill:#333;}#mermaid-svg-fEYA6Fr4AERY9282 .cluster span{color:#333;}#mermaid-svg-fEYA6Fr4AERY9282 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-fEYA6Fr4AERY9282 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-fEYA6Fr4AERY9282 rect.text{fill:none;stroke-width:0;}#mermaid-svg-fEYA6Fr4AERY9282 .icon-shape,#mermaid-svg-fEYA6Fr4AERY9282 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-fEYA6Fr4AERY9282 .icon-shape p,#mermaid-svg-fEYA6Fr4AERY9282 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-fEYA6Fr4AERY9282 .icon-shape .label rect,#mermaid-svg-fEYA6Fr4AERY9282 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-fEYA6Fr4AERY9282 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-fEYA6Fr4AERY9282 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-fEYA6Fr4AERY9282 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Ray Core

Task · Actor · Object
Ray Data

分布式数据加载 / 预处理
Ray Train

分布式训练
Ray Tune

超参搜索
Ray Serve

模型服务
RLlib

强化学习
KubeRay

K8s 编排
Anyscale

托管平台

  • Ray Data:分布式数据集,承担 ML 流水线里的数据摄取、预处理与 shuffle,被 Train / Tune / Serve 共用。
  • Ray Train :分布式训练的抽象层,对 PyTorch / TensorFlow / HuggingFace 等原生框架做封装,提供 TorchTrainer 这类高层 API。
  • Ray Tune:超参数优化,内建 ASHA、HyperOpt、Optuna、贝叶斯等搜索算法与调度策略。
  • Ray Serve:可伸缩的模型服务,支持部署编排、模型组合、自动扩缩,对外暴露 HTTP / gRPC。
  • RLlib:工业级强化学习库,覆盖 PPO、IMPALA、DQN 等主流算法。
  • KubeRay :Ray 在 Kubernetes 上的 Operator,管理 RayCluster / RayService / RayJob
  • Anyscale:Ray 的商业化托管形态。

8. 适用边界与同类对比

Ray 不是银弹。把它的能力和相近系统放在一起看,边界会更清楚:

维度 Ray Spark Dask Celery
任务图 运行时动态构建 提交时静态 DAG 惰性 DAG 无(链式)
有状态单元 原生 Actor
共享内存对象 Plasma 零拷贝 块级内存 无(走 broker)
调度延迟 毫秒级 秒级 秒级 低(但无全局调度)
容错 谱系重建 + GCS 重算 Stage 重跑图 重试 + ack
自动扩缩 内建(控制器) 依赖外部 手动 手动 / 外部
最适合 ML 流水线、推理、RL 批处理 ETL 交互式数据探索 简单后台任务队列

选型上可以这么看:

  • 实时推理且要保住模型状态 → Ray Actor + Object Store,零拷贝共享模型、带状态批处理。
  • 超参搜索 / 分布式训练 → Ray Tune + Ray Train,原生打通。
  • 大规模 ETL 批处理 → Spark 仍更顺手,DAG 静态、生态成熟。
  • 交互式 Pandas 式探索 → Dask 更轻、API 更亲和。
  • 单纯的请求队列 / 后台任务 → Celery 运维成本更低,没必要上 Ray。

几个工程上容易踩的坑值得单独提:Head 节点 / GCS 在没开容错时是单点 ,生产务必启用 GCS FT 或用 KubeRay 做 leader 选举;对象存储 OOM 是头号静默故障,记得限制 object_store_memoryActor 放置策略 影响 P99 延迟,相关 Actor 用 placement_group 的 bundle 共置到同一节点能显著降低跨节点开销。


9. 小结

Ray 的巧妙之处在于,它没有试图发明一套全新的分布式范式,而是把"任务并行"和"actor"这两种早已存在的抽象,塞进了同一张动态任务图,再配上一套与之匹配的系统架构:GCS 把控制状态集中存储却与调度逻辑解耦,Plasma 用共享内存抹平节点内数据搬运,自底向上的调度在延迟和全局视野之间取了务实的平衡。

代价同样明确------它为大块、长寿命、适度共享的 ML 对象而优化,在海量小对象或极端 fan-out 场景下反而吃亏;所有权式的 GC 和谱系容错在 owner 失效时会暴露局限;Head 节点的单点问题虽然已有缓解方案,但仍需要运维显式配置。

对工程师而言,Ray 真正的价值不是"又一个分布式框架",而是它把训练、调参、服务这些原本要用好几套系统拼起来的 AI 工作负载,收敛到了一个统一的计算层上。理解了 Core 的三个原语和背后的四层架构,上层那一整套库就不再神秘------它们都只是 Task 和 Actor 的不同编排方式罢了。

相关推荐
DevNo1 小时前
学生党课堂复习利器:三款音视频转文字工具实测对比
人工智能
谢白羽1 小时前
Mooncake在LLM的kv cache offload
人工智能·llm·论文·agent·mooncake
IT_陈寒1 小时前
Vue的v-for为啥把我的渲染顺序搞乱套了?
前端·人工智能·后端
Yangs_noerr1 小时前
Hermes Agent 打造私人 AI 助手
人工智能
慕容引刀1 小时前
或许你真的需要这个Git神器:让团队提交文案终于对齐了
人工智能·git·vscode·自然语言处理·github
Jaeger_1 小时前
MCP 配置到底要重复配几次?一次配置,Claude/Cursor/Codex 全通用
人工智能
Python私教1 小时前
四个入口,一条流水线:如意智影的多入口架构取舍
人工智能·python·架构
软件聚导航1 小时前
软件聚导航-前端工程师的“小家庭”,工具与生活并存
人工智能·小程序·学习方法·图片压缩
撑伞的鱼99371 小时前
零成本AI编程实践,我花0元拿到了不限量 Token
人工智能·个人开发·免费·ai编程