# 数据多副本

## 概述

Alluxio 工作节点上文件数据和元数据的副本称为**副本**。文件副本功能允许通过在不同工作节点上创建和分发多个副本，从而让多个工作节点能够提供同一个文件。此功能主要用于提高 I/O 吞吐量和可用性，特别是在数据集被大量客户端并发缓存和访问时。

如果未启用文件副本（即副本级别设置为 1），一个文件在集群中一次只会有一个副本。这意味着所有客户端对该文件数据的访问都将由存储该副本的单个工作节点提供服务。在许多客户端需要并发访问此文件的情况下，该单个工作节点可能会成为性能瓶颈。

> **注意：** 一个工作节点对于任何给定的文件最多只会有一个副本，它不会存储同一个文件的多个副本。

## 启用文件副本

文件副本由几个系统设置控制，这些设置决定了创建多少副本以及如何访问它们。

### 副本数量

默认情况下，副本级别为 1，表示每个文件只有一个副本。要增加副本数量，你有两个选择：

* **创建副本规则（推荐）**：此方法通过允许你为特定路径定义副本级别，从而提供精细的控制。例如，一个规则可以为给定路径下的所有文件将副本数设置为 3。
* **设置集群范围的默认值**：使用已弃用的 `alluxio.user.file.replication.min` 属性为整个集群设置默认副本级别。此方法不如使用副本规则灵活。

### 副本选择策略

当读取具有多个副本的文件时，**副本选择策略**决定了客户端从哪个工作节点读取。这对于负载均衡和确保高可用性至关重要。如果所选工作节点不可用，客户端会自动故障转移到另一个副本。

该策略使用 `alluxio.user.replica.selection.policy` 属性进行配置，可用选项如下：

* `GLOBAL_FIXED`: 所有客户端按相同的固定顺序从工作节点读取。
* `CLIENT_FIXED`: 每个客户端都有自己固定的工作节点顺序，从而将负载分散到各个副本。
* `RANDOM` (默认): 每个客户端为每个读取请求选择一个随机副本。

要配置该策略，请设置以下属性：

```properties
alluxio.user.replica.selection.policy=GLOBAL_FIXED
```

### 优化副本读取性能

默认情况下，客户端遵循副本选择策略，而不考虑所选工作节点上是否已缓存数据，这可能导致不必要的冷读取。为了优化这一点，你可以启用两个关键功能：**缓存副本优先**和**被动缓存**。

* **缓存副本优先**：客户端首先检查哪些可用副本工作节点已完全缓存了文件。它会优先从这些“热”工作节点之一读取，以避免从 UFS 进行缓慢的冷读取。
* **被动缓存**：此功能补充了副本优先。如果客户端最偏好的工作节点没有缓存数据，但另一个工作节点有，客户端将从缓存源读取，同时触发一个异步后台作业将文件加载到偏好的工作节点上。这确保了数据在理想的工作节点上被缓存，以供将来读取。

要启用这两个功能，请设置以下属性：

```properties
alluxio.user.replica.prefer.cached.replicas=true
alluxio.user.file.passive.cache.enabled=true
```

> 注意：部分缓存文件的工作节点不会比完全没有缓存的工作节点更优先。

## 配置副本规则

文件的副本数量通过**副本规则**进行管理。这些规则通过Coordinator上的 REST API 端点进行配置。

**API 端点：** `http://<coordinator_host>:<coordinator_api_port>/api/v1/replica-rule`

### 创建副本规则

要添加新规则，请发送一个 `POST` 请求，其 JSON 正文指定 `path` 和 `numReplicasPerCluster`。

以下命令为根路径 (`/`) 下的所有文件将副本级别设置为 2：

```shell
curl -X POST -H 'Content-Type: application/json' -d '{"path": "/", "numReplicasPerCluster": 2}' http://<coordinator_host>:<coordinator_api_port>/api/v1/replica-rule
```

你可以为不同的路径创建多个规则。例如，为 `/s3/vip_data` 设置 3 个副本，为 `/s3/temp_data` 设置 1 个副本：

```shell
curl -X POST -H 'Content-Type: application/json' -d '{"path": "/s3/vip_data", "numReplicasPerCluster": 3}' http://<coordinator_host>:<coordinator_api_port>/api/v1/replica-rule
curl -X POST -H 'Content-Type: application/json' -d '{"path": "/s3/temp_data", "numReplicasPerCluster": 1}' http://<coordinator_host>:<coordinator_api_port>/api/v1/replica-rule
```

{% hint style="warning" %}
**嵌套路径限制：** 目前，副本规则路径不能嵌套。例如，你不能同时为 `/parent` 和 `/parent/child` 设置规则。这也意味着根路径 (`/`) 上的规则不能与任何其他规则共存。此限制将在未来版本中取消。
{% endhint %}

### 列出副本规则

要查看所有活动的副本规则，请向该端点发送 `GET` 请求：

```shell
curl -G http://<coordinator_host>:<coordinator_api_port>/api/v1/replica-rule
```

响应是一个包含所有规则的 JSON 对象。以下示例显示了根路径 (`/`) 上的默认规则，副本级别为 1。

```json
{
  "/": {
    "pathPrefix": "/",
    "clusterReplicaMap": {
      "DefaultAlluxioCluster": 1
    },
    "totalReplicas": 1,
    "type":"UNIFORM"
  }
}
```

### 更新副本规则

要更新现有规则，请发送一个具有相同路径但 `numReplicasPerCluster` 值不同的 `POST` 请求。以下示例将 `/s3/vip_data` 的规则更新为 1 个副本：

```shell
curl -X POST -H 'Content-Type: application/json' -d '{"path": "/s3/vip_data", "numReplicasPerCluster": 1}' http://<coordinator_host>:<coordinator_api_port>/api/v1/replica-rule
```

### 删除副本规则

要删除规则，请发送一个 `DELETE` 请求，其 JSON 正文中包含要删除规则的路径：

```shell
curl -X DELETE -H 'Content-Type: application/json' -d '{"path": "/s3/vip_data"}' http://<coordinator_host>:<coordinator_api_port>/api/v1/replica-rule
```

## 创建副本

副本可以通过两种方式创建：通过分布式加载作业主动创建，或在客户端读取数据时被动创建。

### 主动创建副本

创建特定数量副本的最可靠方法是使用 [`job load`](/ee-ai-cn/ai-3.8-15.1.x-cn/cache/loading-data-into-the-cache.md) 命令。此命令会启动一个分布式作业，将文件加载到 Alluxio 中，并创建与匹配的副本规则指定的副本数量。

例如，如果为 `/s3` 设置的规则为 3 个副本，以下命令将加载 `/s3/file`，并在 3 个不同的工作节点上创建 3 个副本：

```console
bin/alluxio job load --path s3://bucket/file --submit
```

> 即使某些副本加载失败，加载作业也会报告为成功。失败的副本将在下一次客户端请求时被动创建。

### 被动创建副本

在两种情况下可以被动创建副本：冷读取时或通过被动缓存。

* **冷读取时** 当客户端读取一个未在 Alluxio 中缓存的文件时，会触发“冷读取”，文件被加载到服务该请求的工作节点的缓存中，从而创建一个副本。如果后续对同一文件的客户端请求被定向到不同的工作节点（基于副本选择策略），则会随着时间的推移被动创建额外的副本。以这种方式创建的副本数量不作保证，并取决于客户端的访问模式。
* **通过被动缓存** 当[被动缓存](#you-hua-fu-ben-du-qu-xing-neng)启用时，读取文件可以触发异步副本创建。如果客户端偏好的worker没有数据，客户端会从另一个源读取，同时一个异步后台作业会将文件加载到偏好的worker。此处被动缓存文件加载的优先级如下。

  1. 同AZ中100%缓存目标文件副本的worker
  2. 不同AZ中100%缓存目标文件副本的worker
  3. UFS

  当一个worker开始执行被动缓存文件加载时，它会首先尝试去满足优先级1的worker中加载副本。如果没有这样的worker，它会转而去满足优先级2的worker中加载副本。如果满足优先级1和优先级2的worker都不存在，那么该worker直接从UFS加载目标文件的副本。这会在偏好的worker上创建一个新副本，确保未来读取时有更好的数据局部性。

## 监控

Alluxio 提供指标来监控文件副本和被动缓存的行为。

### 读取流量指标

当 `alluxio.user.replica.prefer.cached.replicas` 启用时，`alluxio_multi_replica_read_from_workers_bytes_total` 指标会跟踪数据读取模式。

* **类型**：`counter`
* **组件**：`client`
* **描述**：客户端从工作节点读取多副本文件的总字节数。它有助于分析读取分布和缓存效率。
* **标签**：
  * `cluster_name`：服务工作节点所在的集群。
  * `local_cluster`：如果工作节点与客户端在同一集群中，则为 `true`。
  * `hot_read`：如果工作节点有完全缓存的副本，则为 `true`。

### 被动缓存指标

当被动缓存启用时，`alluxio_passive_cache_async_loaded_files` 指标会跟踪异步加载的文件。

* **类型**：`counter`
* **组件**：`worker`
* **描述**：由被动缓存触发的工作节点加载的文件总数。
* **标签**：
  * `result`：加载作业的结果（`submitted`、`success` 或 `failure`）。

## 限制

### 可变数据的一致性

文件副本最适合不可变数据。如果底层 UFS 中的数据是可变的，则在不同时间加载的不同副本可能会变得不一致。

为了缓解这种情况，你可以：

1. **手动刷新元数据**：使用带有 `--metadataOnly` 标志的 `job load` 命令检查 UFS 更新并使过时的副本失效。

   ```console
   bin/alluxio job load --path s3://bucket/file --metadataOnly --submit
   ```
2. **使用缓存过滤器策略**：设置 `maxAge` 策略以在一定时间后自动使副本失效，强制从 UFS 重新加载它们。例如，10 分钟的 `maxAge`：

   ```json
   {
     "apiVersion": 1,
     "data": { "defaultType": "maxAge", "defaultMaxAge": "10min" },
     "metadata": { "defaultType": "maxAge", "defaultMaxAge": "10min" }
   }
   ```

### 副本级别不会被主动维护

Alluxio 不会主动添加或删除副本以匹配规则中设置的目标副本级别。实际副本数量可能由于以下原因而低于或高于目标：

* **缓存驱逐**：由于缓存容量不足，副本可能会从工作节点中被驱逐。
* **被动创建**：如果文件总是从现有副本提供服务，则可能不会创建新副本。
* **工作节点成员资格变更**：当工作节点加入或离开集群时，副本选择算法可能会改变，导致“孤立”的副本不再被选中但仍存在于缓存中。这些孤立的副本最终会被驱逐。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://documentation.alluxio.io/ee-ai-cn/ai-3.8-15.1.x-cn/high-availability/multiple-replicas.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
