# Replication: cluster creation, joining, updating table settings

Replication: creating a cluster, connecting a node, changing table settings and applying them to old documents

### 关于我

<div style="display: flex; align-items: flex-start; padding: 0; margin: 0;">
  <img src="./mike-replication/mike.jpg" alt="Mike" style="max-width: 50%; max-height: 400px; margin: 0; padding: 0;">
  <div style="margin-left: 20px;">
    <p>大家好，我是迈克。</p>
    <p>我最近开始在Manticore担任开发倡导者。我并不是完全远离IT的人，但正在追赶现代技术。在这篇博客中，我将分享我的经验和学习Manticore的体会。我计划以日记的形式记录我的旅程，解释Manticore是什么以及如何使用它。让我们一起发现事物如何协同工作，识别问题，并实时与开发者互动。</p>
    <p>这是我的第一篇博客文章。如果您有兴趣与我一起学习Manticore，我会通过以下方式保持更新：
    <ul>
      <li>
      <a href="https://twitter.com/manticoresearch">推特</a>
      </li>
      <li>
      电报： <a href="https://t.me/manticoresearch_en">EN</a> / <a href="https://t.me/manticore_chat">RU</a>
      </li>
      <li>
      <a href="https://slack.manticoresearch.com/">Slack</a>
      </li>
    </ul>
  </div>
</div>

### 复制

复制的一般目的是什么？

在[上一篇文章](/blog/mike-replace-update-wordforms/)中，我们通过替换词形文件来更新全文搜索设置。在进行此操作时，我们的表不可用。以我们商店的示例来看，停机时间很短——只有几秒钟。对于小型项目，这种短暂的中断可能是增强便利性的微不足道的代价。然而，随着我们的商店产品和客户数量增加，服务器设置的更改现在可能导致持续数小时的系统停机时间。数据库越大，重新索引的过程就越长。虽然使用Manticore重新索引只需几个小时——而不是几天或几周——我们仍希望避免这种短暂的延迟。此外，如果我们的单一服务器发生故障，或者客户端数量变得太大以至于一台服务器无法处理，所有高速搜索引擎的优势都将丧失。因此，我们现在考虑创建多个同时并行运行的数据库副本。这种设置将确保当数据写入一个服务器时，它会自动复制到其他连接的服务器或节点上。

在Manticore中，节点之间的复制通过[Galera库](https://github.com/codership/galera)实现。Galera使用同步多主复制技术，为数据库集群提供高可用性和容错能力。当在其中一个服务器（节点）上添加新记录时，更改会立即传输到所有连接的节点。此过程包括三个阶段：在源节点上写入本地事务日志，将更改复制到其他节点，并在所有节点确认收到数据后才实际应用事务。只有在收到集群中所有节点的确认后，事务才会被应用，这确保了所有节点上的数据一致性。对于用户来说，这些过程是不可见的，任何节点在事务成功完成后都能立即访问新数据。

### 初始设置

目前，我们从[上一篇文章](/blog/mike-replace-update-wordforms/)中的容器运行良好，但如果它停止并被删除，数据将永远丢失。[Manticore Docker指南](https://github.com/manticoresoftware/docker)建议将数据目录映射到容器外部。此外，我们上次的配置未包含用于复制所需的二进制端口9312。让我们通过创建正在运行容器的文件夹快照并启动一个具有正确端口和存储设置的新容器来修复此问题。

首先，我们需要确保数据完整性。我们进入容器，登录Manticore并“冻结”表，这样所有可能在内存中的数据将可靠地移动到磁盘，并且在复制过程中磁盘上的内容不会发生变化。
```
FREEZE products;
```

接下来，从容器中复制数据目录：
````
docker cp Manticore:/var/lib/manticore .
````

该命令由三部分组成：
- `cp` - 复制，
- `Manticore:/var/lib/manticore` - 容器名称和容器内的文件夹路径，
- `.` - 要复制到的本地路径，在这种情况下是当前目录。

为了让当前容器继续像以前一样工作，我们“解冻”表：
```
UNFREEZE products;
```

现在，让我们创建一个具有所需设置的新容器：

```
docker run —name manticore_new -v $(pwd)/manticore:/var/lib/manticore -p 10306:9306 -p 10312:9312 -d manticoresearch/manticore
docker exec -it manticore_new mysql
```
结果，我们有了一个带有转发端口和数据库文件位于容器外部的容器克隆。让我们检查新容器中的所有内容是否就绪：

![新容器视图](./mike-replication/pic1.png)

很好，所有数据都已转移，包括配置和词形文件，现在我们将基于此容器创建一个集群。

{{<notice "info">}}
顺便提一下，关于词形文件的一点说明：在我们复制的文件夹中，有一个此文件的副本。我建议将其复制到外部，因为根据实践经验，未来您可能需要编辑它，而使用位于表文件夹中的文件是一个坏主意，最终可能导致一些问题。我将副本放在数据库文件夹之外：`cp manticore/products/wf_pet_products.txt wf_pet_products.txt`。还有一个好消息，我和我的同事讨论过——不久之后，使用`mysqldump`时您将不再需要手动移动词形文件，所有内容将自动保存在转储中。这是GitHub上的[任务](https://github.com/manticoresoftware/manticoresearch/issues/2046)。
{{</notice>}}

#### 创建我们的第一个集群

要实现我们的新集群，不需要复杂的操作——只需使用一条命令创建一个带有名称的集群，然后将必要的表附加到它即可。之后，我们只需检查一切是否设置正确。

* 要添加集群，请使用 `CREATE CLUSTER` 命令。
* 要将表添加到集群，请使用 `ALTER CLUSTER ADD` 命令。**需要注意的是，聚类、复制和其他 Manticore 功能仅适用于 [实时表](https://manual.manticoresearch.com/Read_this_first#Real-time-table-vs-plain-table)！**

那么，让我们创建我们的第一个集群，并立即将其表添加到其中：

```
create cluster pet_shop;
alter cluster pet_shop add products;
```

现在让我们检查一下我们得到了什么。为此，请使用 `show status` 命令，但它会提供大量信息，为了避免迷失其中，可以使用带有 `like` 操作符的过滤器：

```
show status like '%cluster%'
```

![显示状态结果](./mike-replication/pic6.png)

目前，我们关注以下几行：`cluster_name`、`cluster_pet_shop_status`、`cluster_pet_shop_indexes`。这些显示了集群的名称、状态（如果显示为 `Primary`，则一切正常），以及当前在集群中的表。
我们还应注意 `cluster_pet_shop_incoming_addresses` 这一行。在我的设置中，它看起来像这样：`172.17.0.5:9312,172.17.0.5:9315:replication`。我们需要地址 `172.17.0.5:9312`。我们已将端口 9312 映射到 Docker 外部的端口 10312，但在示例中，我们将在一个名为 `172.17.0.0` 的相同 Docker 网络中运行一个新节点，使端口使用更简单。

{{<notice "info">}}
从技术上讲，我们可以通过仅将集群添加到它并连接表，使用原始容器 [来自关于 Wordforms 的文章](https://chatgpt.com/blog/mike-replace-update-wordforms/)。然后我们可以创建一个带有外部存储的新容器，复制会将所有内容复制到本地镜像。但这样我就无法展示通过从容器中复制文件来保存转储的另一种解决问题的方法了 =)
我们自己铺床，然后自己躺进去。
{{</notice>}}

第一个节点已经设置好，不需要其他额外操作。简单？这简直是小菜一碟！

#### 向集群添加另一个节点

首先，我们需要启动另一个带有 Manticore 的容器。我们不会向其中传输任何内容，只需将其连接到现有集群。本地存储必须不同（如果您在同一服务器上执行此操作，连接的文件夹也应不同）。需要再次提醒关于端口的问题，因为我们已经使用了端口 9306、10306 和 10312。因此，让我们分配不同的端口，例如 11306 和 11312。

我们创建另一个带有 Manticore 实例的容器，将其命名为 `Manticore_new_1`。我们指定端口 11306 和 11312，对于卷，我们指定 manticore_new_1（本地文件夹必须已经存在）。

![新节点](./mike-replication/pic7.png)

或者所有内容都在一条命令中：

```
docker run --name manticore_new_1 -v $(pwd)/manticore_new_1:/var/lib/manticore -p 11306:9306 -p 11312:9312 -d manticoresearch/manticore
```

通过 MySQL 客户端登录。这里有一个细节：如果您使用本地 MySQL 客户端连接，而不是容器内的一个，则使用创建节点时指定的外部端口 — 11306。如果您使用 Docker 接口并通过容器终端进入（`docker exec`），则使用 Manticore 的默认端口 — 9306。无论如何，连接。是否有表（`show tables`）。结果如预期为空，因为我们刚刚创建了一个空的 Manticore 容器。现在将其连接到现有集群 — `join cluster pet_shop at '172.17.0.5:9312';`

![连接到集群并检查参数](./mike-replication/pic9.png)

*为了清晰起见，我为第二个节点更改了控制台颜色。*

正如我们所看到的，表已添加，记录数量与原始节点匹配，并且词干和词形文件的配置是正确的。
基本上，就是这样。集群已经组装并运行，数据在节点之间传输。

{{<notice "warning">}}
**重要说明。** 如果连接到集群的节点具有与集群中表同名的表，则节点上的表将被集群中的数据覆盖。集群数据的优先级高于本地表，因此如果您连接的现有节点已经有数据，请确保现有表的名称与集群中的表不同。一如既往，在任何有风险的情况下，[进行备份](https://manual.manticoresearch.com/Securing_and_compacting_a_table/Backup_and_restore#Backup-and-Restore)。
{{</notice>}}

#### 在集群中管理数据

在集群中处理表数据时，有一些差异。插入命令现在需要一些调整 — 除了表名外，我们需要指定其对应的集群名称：`insert into <cluster name>: <table name>(<fields>) values (<values>)`。不要忘记在客户端中更新此命令。

让我们在新创建的节点中添加另一条记录：
```
insert into pet_shop:products (name, info, price, avl) values ('Aquarium ship', 'Decorative ship model for aquarium', 6, 1);
```

![添加新记录的结果](./mike-replication/pic10.png)

根据结果，记录已添加，但另一个节点呢？
![第二个节点的结果](./mike-replication/pic11.png)

这里一切也正常！
让我们尝试更新数据：
```
mysql> update products set price = 8.0 where id = 3317338896206921730;
ERROR 1064 (42000): table products: table 'products' is a part of cluster 'pet_shop', use 'pet_shop:products'
```
对于更新、更改和特别是删除记录，我们现在也需要在表名中指定集群名称：
```
update pet_shop:products set price = 8 where id = 3317338896206921730;
Query OK, 1 row affected (0.01 sec)
```
因此，数据现在在节点之间自动且无需太多复杂性地传输，除了写入命令的小改动。

#### 更改复制表的设置

如果我们需要更改表配置或删除它，例如更新词形文件，会怎样呢？在[上一篇文章](/blog/mike-replace-update-wordforms/)中，我们必须删除并重新创建表，导致用户在一段时间内无法获得服务器响应。在那个例子中，更新设置所需的时间非常短，因为表很小。但使用更大的数据集，例如包含数百万或数十亿条记录的表，更新和索引可能需要很长时间，通常以小时为单位。为了确保基于Manticore的应用程序服务不间断，有[分布式表](https://manual.manticoresearch.com/Creating_a_table/Creating_a_distributed_table/Creating_a_distributed_table)，但我们将在另一篇文章中讨论这一点。

目前，我们有一个跨多个节点的复制数据库，其中包含`products`表。我们可以通过使用集群名称前缀来更改此表的配置，但即使使用前缀也无法删除它。要更改复制表的设置，首先需要将其从集群中分离：`ALTER CLUSTER <cluster name> DROP <table name>`。这将仅从集群中删除表，而不是从数据库中删除。在表从集群中分离后，应用程序将无法更新数据，因为它引用了集群（例如，`insert into pet_shop:products ...`），而表不再在其中（应用程序应处理这种情况）。现在我们可以删除或重新配置表。

例如，让我们更新表配置：从词干提取器切换到词形还原器。以下是步骤：
* 将表从集群中分离。
* 将表中的形态学从词干提取器更改为词形还原器。
* 将数据重新加载到表中。
* 在集群中恢复表。
* 在第二个节点上检查。

从集群中分离表：
```
ALTER CLUSTER pet_shop DROP products;
```
现在，集群中所有节点上的表都已与之断开连接，其模式和设置可以修改。我们的工作逻辑意味着在一个节点上执行一些技术操作，而另一个节点则为用户提供`select`查询。作为一种保护措施，添加新记录将不再可能，因为应用程序使用`<cluster>:<table>`格式的命令，而此表不再在集群中。
```
update pet_shop:products set price = 9 where id = 3317338896206921730;
ERROR 1064 (42000): table products: table 'products' is not in any cluster, use just 'products'
```
在我们从集群中分离表后，尝试执行`select`查询：

![其他节点的结果](./mike-replication/pic13.png)

正如我们所见，查询已处理，数据已提供，最终用户应该感到满意。

现在，让我们将形态学从词干提取器更改为词形还原器，重新索引记录，并重新连接一切。在[上一篇文章](/blog/mike-replace-update-wordforms/)中，我们通过一些粗略的方法替换了词形文件和词干提取器。在这里，我们将使用更文明的工具。所有替换词形文件或更改表中使用的形态学的操作都可以通过一个命令完成：`ALTER TABLE <table name> morphology='<morph type>'`。让我们将我们的词干提取器替换为词形还原器：
```
ALTER TABLE products morphology='lemmatize_en_all';
```
在更改与数据库文本预处理相关的任何参数后，必须重新索引所有现有记录，以便为旧文档应用形态学和其他分词设置：
```
mysqldump -P9306 -h0 --replace --skip-comments manticore products | mysql -P9306 -h0;
```
{{<notice "info">}}
在这里，我们使用mysqldump技术，将转储输出直接重定向到Manticore的MySQL中。`--replace`选项强制mysqldump生成REPLACE命令而不是INSERT，使我们能够一次性“重新加载”整个表。请注意，对于大型表或在较弱的服务器上执行此命令可能需要很长时间，但这不会让我们太担心，因为我们有一个当前正在为用户提供请求的备份节点，且mysqldump命令不会阻塞表。
{{</notice>}}

在使用`products`表进行这种简单的表重新配置后，我们得到了一个新版本：

![新表](./mike-replication/pic14.png)

新设置和所有数据已应用，现在将此表重新添加到集群中：
```
ALTER CLUSTER pet_shop ADD products;
```
就这样，现在表在所有服务器上已更新，而我们在配置和检查一切以确保其正常工作时，数据始终可以通过第二个节点提供给用户。

![其他节点](./mike-replication/pic15.png)

{{<notice "info">}}
需要注意的是，如果所有节点都失败，整个集群的恢复过程非常重要——如果恢复顺序错误，可能会导致所有设置丢失。详细的恢复方法在[文档](https://manual.manticoresearch.com/Creating_a_cluster/Setting_up_replication/Cluster_recovery)中有描述。
{{</notice>}}
顺便说一下，您可以在我们的互动课程[play.manticoresearch.com.](https://play.manticoresearch.com/replication/)中轻松地玩转复制功能。

今天就到这里！前方风平浪静！这是Mike，祝您好运！
