# 管理数据副本

对于 EMQX 集群，持久存储通过多个数据复本 (replication) 来实现数据的高可用性。如果一个节点崩溃，客户端可以立即连接到一个新节点，并从其他节点上的数据副本中恢复数据。本页为您提供了如何设置数据复本 (replication) 并确保 EMQX 持久存储高可用性的指导，主要分为两部分：设置具有持久存储的新 EMQX 集群和升级现有集群以启用持久存储。

## 集群初始设置

在集群的初始设置过程中，有几个配置参数将影响持久存储如何建立以及数据复制如何开始。这些参数在运行时无法更改，一旦初始化了持久存储，对其进行修改将不会产生任何影响。

### 复制因子 (replication factor)

复制因子由 `durable_storage.<DB>.replication_factor` 配置参数控制，它确定了集群中每个分片应该具有的副本数量。默认值为 `3`。

建议将复制因子设置为奇数，因为它影响到成功写入操作所需的副本数量。较高的复制因子意味着数据的副本分布在集群中的数量更多，从而提高了高可用性。但是，这也会增加存储和网络开销，因为需要进行更多的通信以达成共识。

在较小的集群中，复制因子并不严格执行。例如，在一个两节点集群中，实际的复制因子是 `2`，因为每个分片都在两个节点上复制，不需要进一步的复制。EMQX 将每个分片的副本分配给集群中的不同节点，以确保冗余。

### 分片 (shard) 数量

内置的持久存储被分割成独立的分片，各个分片之间相互独立地复制。较多的分片数量允许更多的 MQTT 消息从持久存储中并行发布和消费。然而，每个分片都会消耗系统资源，比如文件描述符，并且会增加每个会话存储的元数据量。

`durable_storage.<DB>.n_shards` 参数控制分片的数量，一旦持久存储被初始化，这个数量就不会改变。

### 站点 (site) 数量

`durable_storage.n_sites` 配置参数确定了必须在线的最小站点数，以便持久存储初始化并开始接受写入。一旦达到了这个最小值，持久存储就会开始向可用站点平衡地分配分片。

默认值为 `1`，意味着每个节点最初可能认为自己是唯一负责数据存储的站点。这种设置针对单节点 EMQX 集群进行了优化。当集群形成时，一个节点的视图最终会占主导地位，导致其他节点放弃他们存储的数据。

在多节点集群中，建议将站点数量设置为初始集群大小，以避免此类冲突。请注意，一旦初始化了持久存储，此参数将无法更改。

## 更改现有集群

现有集群可能需要重新配置，原因可能是容量、可靠性或客户端流量的变化，或者需要停用旧节点并用新节点替换它们。可以通过添加新站点到具有持久存储数据复本的站点集合中，或者移除不再需要的站点来实现这一点。

您可以使用 `emqx ctl` CLI 并带有 `ds` 子命令来查看当前的分片分配情况：

```shell
$ emqx ctl ds info
SITES:
...
SHARDS:
...
```

### 添加站点

当新节点加入集群时，它会被分配一个 *站点 ID*，并可以纳入持久存储中。一些分片的复制任务将转移到新站点，然后它将开始复制数据。

```shell
$ emqx ctl ds join all <Site ID>
ok
```

根据集群的数据量，加入新站点的过程可能需要一些时间。虽然这个过程不会影响持久存储的可用性，但它可能会暂时影响集群的性能，因为站点之间的后台数据传输。

对持久存储站点集合的任何更改都是持久存储的，确保节点重启或网络分区不会影响结果。集群最终会以一种一致的方式达到期望的状态。

### 移除站点

移除一个站点意味着将分片的复制任务从被移除的站点转移出去。类似于添加站点，此过程可能需要一些时间和资源。

```shell
$ emqx ctl ds leave all <Site ID>
ok
```

移除站点可能导致有效复制因子低于配置的值。例如，如果复制因子为 `3`，而一个 3 节点集群中的一个站点被移除，那么有效复制因子将有效降低到 `2`。为了避免在永久替换站点时出现此风险，建议先添加一个新站点，然后再停用旧站点，或者同时执行这两个操作。

### 分配站点

可以在单个操作中执行一系列更改，以设置持久存储副本所在站点的集合。

```shell
$ emqx ctl ds set-replicas all <Site ID 1> <Site ID 2> ...
```

这种方法可以最大程度地减少站点之间的数据传输量，同时确保尽可能地维持复制因子。

## 灾难恢复

当灾难发生时，知道如何高效地进行节点的恢复对于维护服务的连续性至关重要。本节提供了从常见灾难场景中恢复节点的指导。

### 完全丢失节点

最常见的灾难场景之一是节点的完全丢失，这可能是由于无法恢复的硬件故障、磁盘损坏或人为错误造成的。如果一个节点完全丢失，集群的可用性会在某种程度上受到影响。可以通过将丢失节点的分片重新分配到集群中的其他健康站点来恢复可用性。

1. 确认节点丢失。

   首先，确保通知集群该节点不再属于集群的一部分。否则，重新分配过程将把该节点视为临时下线，这可能导致某些转换无限期挂起。

   ```shell
   $ emqx ctl cluster force-leave emqx@n2.local
   ```

2. 启动分片转换。

   接下来，恢复可用性的方法是将丢失节点的分片重新分配到集群中其他节点。您可以使用标准的 `leave` 命令来实现这一点。即使丢失的节点不可访问，该命令仍然可以运行，但转换可能需要更长时间完成。

   ```shell
   $ emqx ctl ds leave all 5C6028D6CE9459C7 # Here, 5C6028D6CE9459C7 is the lost node's Site ID
   ```

3. 监控集群状态并等待所有分片转换成功完成。您可以使用 `info` 命令检查哪些转换仍在进行中。

   ```shell
   $ emqx ctl ds info
   <...>

   SITES:
   .------------------.-------------------.----------.
   : Site             : Node              : Status   :
   :------------------:-------------------:----------:
   : D8894F95DC86DFDB : 'emqx@n1.local'   : up       :
   : 5C6028D6CE9459C7 : 'emqx@n2.local'   : (!) LOST :
   : <...>

   SHARDS:
   .------------.----------------------.------------------------.
   : DB/Shard   : Replicas             : Transitions            :
   :------------:----------------------:------------------------:
   :-messages/0-:----------------------:------------------------:
   :            : 5C6028D6CE9459C7 (!) : - 5C6028D6CE9459C7 (!) :
   :            : <...>                : + D8894F95DC86DFDB     :
   : <...>
   ```

   在继续进行下一步之前，确保没有更多的转换。

4. 转换完成。一旦所有分片转换完成，您需要告知集群丢失的节点不会返回。

   ```shell
   $ emqx ctl ds forget all 5C6028D6CE9459C7
   ```

   如果计划使用原始节点名称替换丢失的节点，这一步至关重要。如果不这样做，可能会导致集群在两个不同的 Site ID 下识别出相同的节点名称，从而导致严重的混淆和潜在的问题。
