> For the complete documentation index, see [llms.txt](https://documentation.alluxio.io/ee-ai-cn/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://documentation.alluxio.io/ee-ai-cn/performance/s3-write-cache.md).

# S3-API 写入优化

{% hint style="warning" %}
此功能自 AI-3.8 起为实验性功能。
{% endhint %}

本指南介绍如何在 [S3 API](/ee-ai-cn/data-access/s3-api.md) 之上启用写缓存，将 `PUT` 请求缓冲至本地 NVMe，并在后台异步持久化至 UFS，实现毫秒级写入延迟。

## 架构概述

S3 API 支持两种部署模式：

|                  | 标准模式（读缓存）              | 写缓存模式                                |
| ---------------- | ---------------------- | ------------------------------------ |
| **适用场景**         | 加速从远程对象存储的读取           | 低延迟写入并异步持久化                          |
| **FoundationDB** | 不需要                    | 需要                                   |
| **写策略**          | WRITE\_THROUGH         | WRITE\_THROUGH、WRITE\_BACK、TRANSIENT |
| **部署复杂度**        | 低                      | 中等 — 需要 FDB 集群和路径级别的策略配置             |
| **典型工作负载**       | AI 模型加载、数据分析、基于 S3 的读取 | 训练检查点、ETL 管道、混合云写缓冲                  |

> 如果您的工作负载以读取为主、偶尔写入，标准读缓存模式已满足需求 — 参见 [S3 API](/ee-ai-cn/data-access/s3-api.md)。

### 写缓存工作原理

写缓存在标准 S3 API 部署基础上引入 **FoundationDB (FDB)** 以提供并发写入场景下的强一致性元数据管理。FDB 处于所有元数据操作的关键路径上。

* **写路径** — `PUT` 请求和 MPU 上传先写入 FDB（元数据），再写入 Worker 本地 NVMe（数据），后台持久化线程随后将数据异步上传至 UFS。
* **读路径** — `GET` 请求查询 FDB 定位数据所在 Worker，从本地 NVMe 读取。缓存未命中时，Worker 从 UFS 拉取数据并本地缓存。

## 开始之前

请在开始前完成以下检查，跳过此步骤是部署失败最常见的原因。

* [ ] **S3 API 已配置并正常运行** — 写缓存在其基础上构建：

  ```shell
  kubectl exec -n <NAMESPACE> alluxio-cluster-coordinator-0 -- \
    alluxio conf get alluxio.worker.s3.api.enabled
  ```

  预期输出：`true`
* [ ] **Alluxio Operator 正在运行**：

  ```shell
  kubectl get pods -n alluxio-operator
  ```

  预期输出：所有 Pod 均为 `Running`

## 部署步骤

### 1. 安装 FDB CRD

在安装或升级前，先在 `alluxio-operator.yaml` 中启用 FDB Operator：

```yaml
fdb-operator:
  enabled: true
```

如果是升级现有 Operator，需手动应用 FDB CRD（Helm 在升级时不会自动安装 CRD）：

```shell
# 幂等操作，可重复执行
kubectl apply -f alluxio-operator/charts/fdb-operator/crds/
```

验证 FDB Operator 正在运行：

```shell
kubectl get pods -n alluxio-operator -l app.kubernetes.io/name=fdb-operator
```

**✅ 成功：** FDB Operator Pod 显示 `READY 1/1`，`STATUS = Running`。

```console
NAME                                          READY   STATUS    RESTARTS   AGE
alluxio-fdb-controller-6cbd5c7c45-xk2pq      1/1     Running   0          2m
```

> 如果找不到该 Pod，请在 `alluxio-operator.yaml` 中设置 `fdb-operator.enabled=true` 后重新执行 `helm upgrade`。

### 2. 启用写缓存

在 `alluxio-cluster.yaml` 中添加以下配置（在 [S3 API 基础配置](/ee-ai-cn/data-access/s3-api.md#di-yi-bu-qi-yong-s3-api) 之上）：

```yaml
apiVersion: k8s-operator.alluxio.com/v1
kind: AlluxioCluster
spec:
  properties:
    alluxio.worker.s3.api.enabled: "true"
    alluxio.write.cache.enabled: "true"
  fdb:
    enabled: true
```

应用配置：

```shell
# 幂等操作，可重复执行
kubectl apply -f alluxio-cluster.yaml
```

**✅ 成功：** Helm/kubectl 无报错，集群进入协调状态。

> 如果出现 `unable to recognize "alluxio-cluster.yaml": no matches for kind "AlluxioCluster"`，说明 Alluxio Operator CRD 未安装，请先重新安装 Operator。

### 3. 验证部署

等待 Worker 就绪（启动通常需要 2–3 分钟）：

```shell
kubectl wait --for=condition=Ready pod \
  -l app.kubernetes.io/component=worker \
  -n <NAMESPACE> --timeout=120s
```

**✅ 成功：** 输出显示所有 Worker Pod 已达到 `Ready` 状态。

```console
pod/alluxio-cluster-worker-0 condition met
```

确认 FDB Pod 正在运行：

```shell
kubectl get pods -n <NAMESPACE> -l foundationdb.org/fdb-cluster-name=alluxio-cluster-fdb-meta
```

**✅ 成功：** `cluster_controller`、`log`、`storage` Pod 均显示 `Running`。

```console
NAME                                                   READY   STATUS    RESTARTS   AGE
alluxio-cluster-fdb-meta-cluster-controller-1-...      2/2     Running   0          2m
alluxio-cluster-fdb-meta-log-1-...                     2/2     Running   0          2m
alluxio-cluster-fdb-meta-storage-1-...                 2/2     Running   0          2m
```

> 如果 FDB Pod 卡在 `Pending` 状态，请检查 PVC 可用性：`kubectl get pvc -n <NAMESPACE>`。FDB 需要支持动态供给的 StorageClass。

确认写缓存在 Coordinator 上已激活：

```shell
kubectl exec -n <NAMESPACE> alluxio-cluster-coordinator-0 -- \
  alluxio conf get alluxio.write.cache.enabled
```

**✅ 成功：** 返回 `true`。

### 4. 测试写入与写后读

```shell
# 创建测试文件
echo "write cache test" > /tmp/test.txt

# 通过 Alluxio S3 API 写入对象
# 将 <LOAD_BALANCER_ADDRESS> 替换为负载均衡器的主机名或 IP。
# 如无负载均衡器，可使用 port-forward 进行测试：
#   kubectl port-forward -n <NAMESPACE> svc/alluxio-cluster-worker 29998:29998
aws s3 cp /tmp/test.txt s3://<BUCKET>/test.txt \
  --endpoint-url http://<LOAD_BALANCER_ADDRESS>:29998 \
  --no-sign-request
```

**✅ 成功：**

```console
upload: /tmp/test.txt to s3://<BUCKET>/test.txt
```

立即读回（从本地缓存响应，非 UFS）：

```shell
aws s3 cp s3://<BUCKET>/test.txt /tmp/verify.txt \
  --endpoint-url http://<LOAD_BALANCER_ADDRESS>:29998 \
  --no-sign-request

diff /tmp/test.txt /tmp/verify.txt
```

**✅ 成功：** `diff` 无输出（文件内容一致）。

> 如果写入返回 `NoSuchBucket`：请通过 `kubectl exec -n <NAMESPACE> alluxio-cluster-coordinator-0 -- alluxio mount list` 确认挂载是否已生效。

## 写入策略

写缓存默认使用 `WRITE_THROUGH`（同步写入缓存和 UFS）。如需低延迟写入，可对特定路径切换为 `WRITE_BACK`。

| 策略              | 行为                        | 适用场景                   |
| --------------- | ------------------------- | ---------------------- |
| `WRITE_THROUGH` | （默认）同时写入缓存和 UFS，两者均成功才返回。 | 数据安全优先；写入延迟受限于 UFS     |
| `WRITE_BACK`    | 立即写入缓存并返回，后台异步持久化至 UFS。   | 低延迟写入，最终一致性            |
| `TRANSIENT`     | 仅缓存，不持久化至 UFS。            | 临时/可重算数据（如 shuffle 输出） |
| `READ_ONLY`     | 禁止该路径的所有写入。               | 防止路径被意外写入              |
| `NO_CACHE`      | 绕过缓存，直接读写 UFS。            | 不需要缓存的路径               |

### 路径级配置

写入策略按路径配置，同一集群内不同路径可使用不同策略。

交互式编辑策略配置（在 Coordinator Pod 内执行）：

```shell
kubectl exec -it -n <NAMESPACE> alluxio-cluster-coordinator-0 -- alluxio pathconfig edit
```

示例配置 — 全局默认 `WRITE_THROUGH`，checkpoint 路径使用 `WRITE_BACK`，shuffle 使用 `TRANSIENT`：

```json
{
  "apiVersion": "v1.0",
  "defaultRule": {
    "description": "Global default",
    "policyMode": "WRITE_THROUGH",
    "properties": {
      "writeReplicas": 1
    }
  },
  "pathRules": [
    {
      "alluxioPath": "/checkpoints/**",
      "description": "Low-latency checkpoint writes",
      "policyMode": "WRITE_BACK",
      "properties": {
        "writeReplicas": 2
      }
    },
    {
      "alluxioPath": "/shuffle/**",
      "description": "Temporary shuffle data",
      "policyMode": "TRANSIENT",
      "properties": {
        "writeReplicas": 2
      }
    }
  ]
}
```

验证路径是否命中预期策略：

```shell
kubectl exec -n <NAMESPACE> alluxio-cluster-coordinator-0 -- \
  alluxio pathconfig test --path /checkpoints/epoch-1/model.pt
```

**✅ 成功：** 输出包含 `"policyMode": "WRITE_BACK"`。

通过 REST API（适合程序化集成）：

```shell
curl -X PUT \
  -H "Content-Type: application/json" \
  -d @pathconfig.json \
  http://<COORDINATOR_HOST>:<COORDINATOR_PORT>/api/v1/conf
```

### 多副本写入

对于 `WRITE_BACK` 和 `TRANSIENT` 路径，设置 `writeReplicas > 1` 可在不同 Worker 上保留多份未持久化数据的副本，降低 UFS 持久化完成前的数据丢失风险。

**权衡**：副本数越高，容错能力和读并发能力越强，但集群内网络带宽消耗和写入延迟略有上升。

推荐设置：

* `WRITE_BACK` — 生产环境 `writeReplicas: 2`；追求最大写入吞吐时用 `1`
* `TRANSIENT` — `writeReplicas: 2` 或更高，因为此类数据不会持久化至 UFS

## 运维与调优

### 关键配置

| 配置项                                                          | 默认值                               | 描述                                                         |
| ------------------------------------------------------------ | --------------------------------- | ---------------------------------------------------------- |
| `alluxio.write.cache.enabled`                                | `false`                           | 启用写缓存。                                                     |
| `alluxio.foundationdb.cluster.file.path`                     | `${alluxio.conf.dir}/fdb.cluster` | FDB cluster 文件路径。通过 Operator 部署 FDB 时自动注入；使用外部 FDB 时需手动指定。 |
| `alluxio.write.cache.async.check.orphan.timeout`             | `1h`                              | 孤儿文件超时。超过此时间仍未提交的写入将被视为已放弃并清理。                             |
| `alluxio.write.cache.async.file.check.period`                | `10min`                           | 孤儿检测扫描周期。设置过短会增加 FDB 负载。                                   |
| `alluxio.worker.page.store.pinned.file.capacity.limit.ratio` | `0.3`                             | 未持久化（Pinned）数据占缓存总容量的最大比例。剩余容量供读缓存使用（LRU 可驱逐）。             |
| `alluxio.worker.mark.writing.files.duration`                 | `10min`                           | 若文件处于写入状态但在该时长内未收到新数据，Worker 将其视为悬空写入并纳入清理。每次写入都会重置计时器。    |

### 异步持久化重试

对于 `WRITE_BACK` 路径，UFS 上传失败时会以**指数退避**方式重试。重试在后台线程中进行，不阻塞前端写入确认。

| 配置项                                                               | 默认值  | 描述              |
| ----------------------------------------------------------------- | ---- | --------------- |
| `alluxio.worker.write.cache.async.persist.retry.initial.interval` | `1s` | 初始重试间隔。         |
| `alluxio.worker.write.cache.async.persist.retry.max.interval`     | `1h` | 最大重试间隔（指数增长上限）。 |

### 监控异步持久化 (15.1.3+)

两条 CLI 命令可用于检查持久化操作的实时状态：

```shell
# 列出指定 Worker 上所有待处理或进行中的文件
kubectl exec -i -n <NAMESPACE> alluxio-cluster-worker-0 -- \
  alluxio async-persist list

# 查询指定路径的持久化状态和重试次数
kubectl exec -i -n <NAMESPACE> alluxio-cluster-coordinator-0 -- \
  alluxio async-persist stat --path /checkpoints/epoch-1/model.pt
```

当 `alluxio fs ls` 显示文件长期处于 `NOT_PERSISTED` 状态时，可使用 `async-persist stat` 判断问题出在队列还是上传阶段。

### 缓存空间管理

Worker 缓存空间分为两个逻辑区域：

* **Pinned 空间**（写缓存）：未持久化的脏数据，不可驱逐。默认上限为总容量的 30%（`alluxio.worker.page.store.pinned.file.capacity.limit.ratio`）。
* **可驱逐空间**（读缓存）：已持久化或从 UFS 加载的数据，空间不足时按 LRU 驱逐。

若持久化吞吐落后于写入流量，Pinned 空间将被填满，Alluxio 会返回 `out-of-space` 错误。预防措施：

* 监控 Pinned 空间使用情况，按需调整 `alluxio.worker.page.store.pinned.file.capacity.limit.ratio`
* 为两个区域分配足够的 NVMe 容量

## 性能参考

以下数据基于 `WRITE_BACK` 模式，测试环境为 AWS `c5n.metal` 客户端 + `i3en.metal` Worker（100 Gbps 网络，NVMe SSD）。实际结果因硬件、对象大小和并发度而异，仅供参考。

| 工作负载                    | 写缓存                         | Direct S3 |
| ----------------------- | --------------------------- | --------- |
| 小对象 PUT（10 KB），低并发      | 3–5 ms                      | 30–60 ms  |
| 小对象 PUT（10 KB），中并发      | 4–9 ms                      | 30–60 ms  |
| 大对象 PUT（10 MB），单 Worker | 稳定 3–6 GB/s                 | 不稳定（受限速）  |
| 写后读 GET 延迟              | 3–7 ms                      | 90–130 ms |
| 异步持久化吞吐                 | \~2,000 objects/s（每 Worker） | —         |

`WRITE_BACK` 的前端写入延迟受限于**本地 NVMe**，而非 UFS。吞吐随 Worker 数量近线性扩展。

## 卸载

按部署的反向顺序移除写缓存配置和 FDB 资源：

**1. 删除 AlluxioCluster**（同时移除 Worker、Coordinator 和 FDB Pod）：

```shell
kubectl delete -f alluxio-cluster.yaml
```

**2. 确认所有 Alluxio Pod 已移除：**

```shell
kubectl get pods -n <NAMESPACE>
```

**✅ 成功：** `No resources found in <NAMESPACE> namespace.`

如需在不删除集群的情况下禁用写缓存，在 `alluxio-cluster.yaml` 中设置 `alluxio.write.cache.enabled: "false"` 后重新应用：

```shell
kubectl apply -f alluxio-cluster.yaml  # 幂等操作
```

## 故障排查

**FDB 启动时连接失败** — FDB Pod 无法从 Worker 访问。

```shell
kubectl get pods -n <NAMESPACE> -l foundationdb.org/fdb-cluster-name=alluxio-cluster-fdb-meta
```

确认 `alluxio.foundationdb.cluster.file.path` 指向有效的 FDB cluster 文件。通过 Operator 部署时会自动注入。

***

**FDB Operator OOM / 内存占用过高** — 默认的 `globalMode: enabled: true` 会使 FDB Operator 监听整个集群的所有 Pod、PVC、ConfigMap 和 Service，在大规模集群中可能导致内存飙升至数 GB。

修复：将 Alluxio Operator、FDB Operator 和 AlluxioCluster 放入**同一 namespace**，在 `alluxio-operator.yaml` 中设置 `globalMode.enabled: false`，然后重启 FDB Operator Pod。

***

**写入时出现 out-of-space 错误** — Pinned（未持久化）数据已填满写缓存。

修复：增大 `alluxio.worker.page.store.pinned.file.capacity.limit.ratio`，扩容 NVMe，或增大 `alluxio.write.cache.async.persist.thread.pool.size` 使持久化速度跟上写入。

***

**WRITE\_BACK 数据未出现在 UFS** — 验证异步持久化线程是否在运行：

```shell
kubectl logs -n <NAMESPACE> -l app.kubernetes.io/component=worker --tail=100 | grep -i persist
```

同时检查 `alluxio.worker.write.cache.async.persist.retry.max.interval` — 若 UFS 不可达，重试可能正处于长时间退避周期中。

***

**孤儿文件持续累积** — 客户端崩溃遗留的未提交写入。缩短 `alluxio.write.cache.async.check.orphan.timeout` 可加快清理，或手动执行：

```shell
kubectl exec -n <NAMESPACE> alluxio-cluster-coordinator-0 -- alluxio job free
```

***

**目录删除返回 DEADLINE\_EXCEEDED** — 对 `WRITE_BACK` 路径执行 `alluxio fs rm -R` 时可能因超时返回 `DEADLINE_EXCEEDED`。尽管出现该错误，文件**可能已在超时前从 UFS 删除**。重试前请直接验证 UFS 状态：

```shell
aws s3 ls s3://<BUCKET>/<path>/ --recursive | head -20
```

如果 S3 中文件已不存在，则删除已成功。重新执行 `rm -R` 将返回 `Path does not exist`。Pagestore 磁盘空间不会立即收缩 — 孤儿页将在下一轮驱逐周期中被回收。

***

**pathconfig 配置未生效** — 验证策略是否正确解析：

```shell
kubectl exec -n <NAMESPACE> alluxio-cluster-coordinator-0 -- \
  alluxio pathconfig test --path <YOUR_PATH>
```

如果路径仍显示旧策略，请检查 Coordinator 日志中的配置重载记录。

***

**持久化后数据未被驱逐** — 驱逐仅在缓存空间承压时触发。如需主动释放空间：

```shell
kubectl exec -n <NAMESPACE> alluxio-cluster-coordinator-0 -- alluxio job free
```

## 参考资源

* [FUSE 写入优化](/ee-ai-cn/performance/fuse-write-cache.md) — 通过 POSIX 文件系统接口使用同一写缓存后端
* [S3 API](/ee-ai-cn/data-access/s3-api.md) — 基础端点、认证、负载均衡和客户端兼容性（启用写缓存前必须完成）
* [S3 UFS 集成](/ee-ai-cn/ufs/s3.md) — 调优底层 S3 持久化层（上传线程、分片设置）
* [S3 API 基准测试](/ee-ai-cn/benchmark/s3-api.md) — 参考基线、工具选型（COSBench / Warp / httpbench）与 S3 API 工作负载调优
