> ## Documentation Index
> Fetch the complete documentation index at: https://clickhouse.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# 数据复制

> 本页介绍一种由五台服务器组成的示例架构。其中两台用于托管数据副本，其余服务器用于协调数据复制

export const Image = ({img, alt, size = "lg"}) => {
  const normalizedSize = ["sm", "md", "lg"].includes(size) ? size : "lg";
  return <div className={`ch-image-${normalizedSize}`}>
      <Frame>
        <img src={img} alt={alt} />
      </Frame>
    </div>;
};

> 在本示例中，你将学习如何搭建一个简单的 ClickHouse 集群，
> 并对数据进行复制。这里共配置了五台服务器，其中两台用于存放
> 数据副本，其余三台用于协调数据复制。

你将要搭建的集群架构如下所示：

<Image img="https://mintcdn.com/private-7c7dfe99/NvnCM4vX9aZ07JxK/images/deployment-guides/replication-sharding-examples/replication.webp?fit=max&auto=format&n=NvnCM4vX9aZ07JxK&q=85&s=2d93be507512432db76164ffa8770669" size="md" alt="使用 ReplicatedMergeTree 的 1 个分片和 2 个副本的架构图" width="1200" height="800" data-path="images/deployment-guides/replication-sharding-examples/replication.webp" />

<Note>
  尽管可以在同一台服务器上同时运行 ClickHouse Server 和 ClickHouse Keeper，
  但我们强烈建议在生产环境中为 ClickHouse Keeper 使用*专用*主机，
  这也是我们将在本示例中演示的方法。

  Keeper server 所需配置可以更小，通常每个 Keeper server 配置 4GB RAM 就足够了，
  至少在你的 ClickHouse Server 规模扩大之前都是如此。
</Note>

<div id="pre-requisites">
  ## 前置条件
</div>

* 你之前已完成[本地 ClickHouse 服务器](/docs/zh/get-started/setup/install)的设置
* 你熟悉 ClickHouse 的基本配置概念，例如[配置文件](/docs/zh/concepts/features/configuration/server-config/configuration-files)
* 你的机器上已安装 Docker

<Steps>
  <Step title="设置目录结构和测试环境" id="set-up">
    <Tip>
      **示例文件**

      以下步骤将引导你从零开始设置集群。如果你想跳过这些步骤，直接开始运行集群，也可以从 examples
      仓库的 ['docker-compose-recipes' 目录](https://github.com/ClickHouse/examples/tree/main/docker-compose-recipes/recipes) 获取这些示例
      文件。
    </Tip>

    在本教程中，您将使用 [Docker compose](https://docs.docker.com/compose/) 搭建 ClickHouse 集群。该方案同样可以修改后适用于独立的本地机器、虚拟机或云实例。

    运行以下命令为本示例设置目录结构：

    ```bash theme={null}
    mkdir cluster_1S_2R
    cd cluster_1S_2R

    # Create clickhouse-keeper directories
    for i in {01..03}; do
      mkdir -p fs/volumes/clickhouse-keeper-${i}/etc/clickhouse-keeper
    done

    # Create clickhouse-server directories
    for i in {01..02}; do
      mkdir -p fs/volumes/clickhouse-${i}/etc/clickhouse-server
    done
    ```

    将以下 `docker-compose.yml` 文件添加到 `cluster_1S_2R` 目录中：

    ```yaml title="docker-compose.yml" theme={null}
    version: '3.8'
    services:
      clickhouse-01:
        image: "clickhouse/clickhouse-server:latest"
        user: "101:101"
        container_name: clickhouse-01
        hostname: clickhouse-01
        volumes:
          - ${PWD}/fs/volumes/clickhouse-01/etc/clickhouse-server/config.d/config.xml:/etc/clickhouse-server/config.d/config.xml
          - ${PWD}/fs/volumes/clickhouse-01/etc/clickhouse-server/users.d/users.xml:/etc/clickhouse-server/users.d/users.xml
        ports:
          - "127.0.0.1:8123:8123"
          - "127.0.0.1:9000:9000"
        depends_on:
          - clickhouse-keeper-01
          - clickhouse-keeper-02
          - clickhouse-keeper-03
      clickhouse-02:
        image: "clickhouse/clickhouse-server:latest"
        user: "101:101"
        container_name: clickhouse-02
        hostname: clickhouse-02
        volumes:
          - ${PWD}/fs/volumes/clickhouse-02/etc/clickhouse-server/config.d/config.xml:/etc/clickhouse-server/config.d/config.xml
          - ${PWD}/fs/volumes/clickhouse-02/etc/clickhouse-server/users.d/users.xml:/etc/clickhouse-server/users.d/users.xml
        ports:
          - "127.0.0.1:8124:8123"
          - "127.0.0.1:9001:9000"
        depends_on:
          - clickhouse-keeper-01
          - clickhouse-keeper-02
          - clickhouse-keeper-03
      clickhouse-keeper-01:
        image: "clickhouse/clickhouse-keeper:latest-alpine"
        user: "101:101"
        container_name: clickhouse-keeper-01
        hostname: clickhouse-keeper-01
        volumes:
         - ${PWD}/fs/volumes/clickhouse-keeper-01/etc/clickhouse-keeper/keeper_config.xml:/etc/clickhouse-keeper/keeper_config.xml
        ports:
            - "127.0.0.1:9181:9181"
      clickhouse-keeper-02:
        image: "clickhouse/clickhouse-keeper:latest-alpine"
        user: "101:101"
        container_name: clickhouse-keeper-02
        hostname: clickhouse-keeper-02
        volumes:
         - ${PWD}/fs/volumes/clickhouse-keeper-02/etc/clickhouse-keeper/keeper_config.xml:/etc/clickhouse-keeper/keeper_config.xml
        ports:
            - "127.0.0.1:9182:9181"
      clickhouse-keeper-03:
        image: "clickhouse/clickhouse-keeper:latest-alpine"
        user: "101:101"
        container_name: clickhouse-keeper-03
        hostname: clickhouse-keeper-03
        volumes:
         - ${PWD}/fs/volumes/clickhouse-keeper-03/etc/clickhouse-keeper/keeper_config.xml:/etc/clickhouse-keeper/keeper_config.xml
        ports:
            - "127.0.0.1:9183:9181"
    ```

    创建以下子目录和文件：

    ```bash theme={null}
    for i in {01..02}; do
      mkdir -p fs/volumes/clickhouse-${i}/etc/clickhouse-server/config.d
      mkdir -p fs/volumes/clickhouse-${i}/etc/clickhouse-server/users.d
      touch fs/volumes/clickhouse-${i}/etc/clickhouse-server/config.d/config.xml
      touch fs/volumes/clickhouse-${i}/etc/clickhouse-server/users.d/users.xml
    done
    ```

    * `config.d` 目录包含 ClickHouse server 配置文件 `config.xml`，
      其中定义了每个 ClickHouse 节点的自定义配置。该
      配置会与每个 ClickHouse 安装自带的默认 `config.xml` ClickHouse 配置
      文件合并。
    * `users.d` 目录包含用户配置文件 `users.xml`，其中
      定义了用户的自定义配置。该配置会与每个
      ClickHouse 安装自带的默认 ClickHouse `users.xml` 配置文件合并。

    <Tip>
      **自定义配置目录**

      最佳实践是，在编写自己的配置时使用 `config.d` 和 `users.d` 目录，
      而不是直接修改 `/etc/clickhouse-server/config.xml` 和 `etc/clickhouse-server/users.xml` 中的默认配置。

      这一行

      ```xml theme={null}
      <clickhouse replace="true">
      ```

      可确保在 `config.d` 和 `users.d`
      目录中定义的配置部分会覆盖默认
      `config.xml` 和 `users.xml` 文件中定义的默认配置部分。
    </Tip>
  </Step>

  <Step title="配置 ClickHouse 节点" id="configure-clickhouse-servers">
    ### 服务器配置

    现在修改位于 `fs/volumes/clickhouse-{}/etc/clickhouse-server/config.d` 的每个空配置文件 `config.xml`。下方高亮显示的行需要根据各节点进行相应修改：

    ```xml highlight={9,53-57} theme={null}
    <clickhouse replace="true">
        <logger>
            <level>debug</level>
            <log>/var/log/clickhouse-server/clickhouse-server.log</log>
            <errorlog>/var/log/clickhouse-server/clickhouse-server.err.log</errorlog>
            <size>1000M</size>
            <count>3</count>
        </logger>
        <display_name>cluster_1S_2R node 1</display_name>
        <listen_host>0.0.0.0</listen_host>
        <http_port>8123</http_port>
        <tcp_port>9000</tcp_port>
        <user_directories>
            <users_xml>
                <path>users.xml</path>
            </users_xml>
            <local_directory>
                <path>/var/lib/clickhouse/access/</path>
            </local_directory>
        </user_directories>
        <distributed_ddl>
            <path>/clickhouse/task_queue/ddl</path>
        </distributed_ddl>
        <remote_servers>
            <cluster_1S_2R>
                <shard>
                    <internal_replication>true</internal_replication>
                    <replica>
                        <host>clickhouse-01</host>
                        <port>9000</port>
                    </replica>
                    <replica>
                        <host>clickhouse-02</host>
                        <port>9000</port>
                    </replica>
                </shard>
            </cluster_1S_2R>
        </remote_servers>
        <zookeeper>
            <node>
                <host>clickhouse-keeper-01</host>
                <port>9181</port>
            </node>
            <node>
                <host>clickhouse-keeper-02</host>
                <port>9181</port>
            </node>
            <node>
                <host>clickhouse-keeper-03</host>
                <port>9181</port>
            </node>
        </zookeeper>
        <macros>
            <shard>01</shard>
            <replica>01</replica>
            <cluster>cluster_1S_2R</cluster>
        </macros>
    </clickhouse>
    ```

    | 目录                                                        | 文件                                                                                                                                                                               |
    | --------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `fs/volumes/clickhouse-01/etc/clickhouse-server/config.d` | [`config.xml`](https://github.com/ClickHouse/examples/blob/main/docker-compose-recipes/recipes/cluster_1S_2R/fs/volumes/clickhouse-01/etc/clickhouse-server/config.d/config.xml) |
    | `fs/volumes/clickhouse-02/etc/clickhouse-server/config.d` | [`config.xml`](https://github.com/ClickHouse/examples/blob/main/docker-compose-recipes/recipes/cluster_1S_2R/fs/volumes/clickhouse-02/etc/clickhouse-server/config.d/config.xml) |

    以下将对上述配置文件的各个部分进行详细说明。

    #### 网络与日志

    通过启用 listen
    host 设置，即可允许通过网络接口进行外部通信。这可确保
    ClickHouse server 主机可被其他
    主机访问：

    ```xml theme={null}
    <listen_host>0.0.0.0</listen_host>
    ```

    HTTP API 端口设为 `8123`：

    ```xml theme={null}
    <http_port>8123</http_port>
    ```

    clickhouse-client 与其他原生 ClickHouse 工具之间，以及 clickhouse-server 与其他 clickhouse-servers 之间通过 ClickHouse 的原生协议进行交互所使用的 TCP 端口设置为 `9000`：

    ```xml theme={null}
    <tcp_port>9000</tcp_port>
    ```

    日志在 `<logger>` 块中定义。以下示例配置将生成一个调试日志，该日志在达到 1000M 时滚动，最多滚动三次：

    ```xml theme={null}
    <logger>
        <level>debug</level>
        <log>/var/log/clickhouse-server/clickhouse-server.log</log>
        <errorlog>/var/log/clickhouse-server/clickhouse-server.err.log</errorlog>
        <size>1000M</size>
        <count>3</count>
    </logger>
    ```

    有关日志配置的更多信息，请参阅默认 ClickHouse [配置文件](https://github.com/ClickHouse/ClickHouse/blob/master/programs/server/config.xml)中的注释。

    #### 集群配置

    集群配置在 `<remote_servers>` 块中定义。
    其中集群名称为 `cluster_1S_2R`。

    `<cluster_1S_2R></cluster_1S_2R>` 块定义了集群的布局，使用 `<shard></shard>` 和 `<replica></replica>` 配置项，并作为 distributed DDL queries 的模板，这些查询通过 `ON CLUSTER` 子句在整个集群中执行。默认情况下，distributed DDL queries 是被允许的，但也可以通过设置 `allow_distributed_ddl_queries` 将其关闭。

    `internal_replication` 设置为 true，这样数据只会写入其中一个副本。

    ```xml theme={null}
    <remote_servers>
        <!-- cluster name (should not contain dots) -->
        <cluster_1S_2R>
            <!-- <allow_distributed_ddl_queries>false</allow_distributed_ddl_queries> -->
            <shard>
                <!-- Optional. Whether to write data to just one of the replicas. Default: false (write data to all replicas). -->
                <internal_replication>true</internal_replication>
                <replica>
                    <host>clickhouse-01</host>
                    <port>9000</port>
                </replica>
                <replica>
                    <host>clickhouse-02</host>
                    <port>9000</port>
                </replica>
            </shard>
        </cluster_1S_2R>
    </remote_servers>
    ```

    对于每台服务器，需要指定以下参数：

    | 参数     | 说明                                                                                                                         | 默认值 |
    | ------ | -------------------------------------------------------------------------------------------------------------------------- | --- |
    | `host` | 远程服务器的地址。可以使用域名、IPv4 地址或 IPv6 地址。如果指定的是域名，服务器会在启动时发起 DNS 请求，并在服务器运行期间一直使用该解析结果。如果 DNS 请求失败，服务器将无法启动。如果更改了 DNS 记录，则需要重启服务器。 | -   |
    | `port` | 用于消息通信的 TCP 端口 (配置中的 `tcp_port`，通常设为 9000) 。不要将其与 `http_port` 混淆。                                                          | -   |

    #### Keeper 配置

    `<ZooKeeper>` 部分用于告知 ClickHouse，ClickHouse Keeper (或 ZooKeeper) 的运行位置。
    由于我们使用的是 ClickHouse Keeper 集群，需要指定该集群中的每个 `<node>`，
    并分别通过 `<host>` 和 `<port>` 标签指定其 hostname 和端口号。

    ClickHouse Keeper 的配置将在本教程的下一步中介绍。

    ```xml theme={null}
    <zookeeper>
        <node>
            <host>clickhouse-keeper-01</host>
            <port>9181</port>
        </node>
        <node>
            <host>clickhouse-keeper-02</host>
            <port>9181</port>
        </node>
        <node>
            <host>clickhouse-keeper-03</host>
            <port>9181</port>
        </node>
    </zookeeper>
    ```

    <Note>
      虽然可以将 ClickHouse Keeper 和 ClickHouse Server 部署在同一台服务器上，
      但在生产环境中，我们强烈建议将 ClickHouse Keeper 部署在专用主机上。
    </Note>

    #### 宏配置

    此外，`<macros>` 部分用于为复制表定义参数替换。这些替换项列于 `system.macros` 中，可在查询中使用 `{shard}` 和 `{replica}` 等替换占位符。

    ```xml theme={null}
    <macros>
        <shard>01</shard>
        <replica>01</replica>
        <cluster>cluster_1S_2R</cluster>
    </macros>
    ```

    <Note>
      这些将根据集群的布局分别定义。
    </Note>

    ### 用户配置

    现在，将以下内容写入位于 `fs/volumes/clickhouse-{}/etc/clickhouse-server/users.d` 的每个空配置文件 `users.xml`：

    ```xml title="/users.d/users.xml" theme={null}
    <?xml version="1.0"?>
    <clickhouse replace="true">
        <profiles>
            <default>
                <max_memory_usage>10000000000</max_memory_usage>
                <use_uncompressed_cache>0</use_uncompressed_cache>
                <load_balancing>in_order</load_balancing>
                <log_queries>1</log_queries>
            </default>
        </profiles>
        <users>
            <default>
                <access_management>1</access_management>
                <profile>default</profile>
                <networks>
                    <ip>::/0</ip>
                </networks>
                <quota>default</quota>
                <access_management>1</access_management>
                <named_collection_control>1</named_collection_control>
                <show_named_collections>1</show_named_collections>
                <show_named_collections_secrets>1</show_named_collections_secrets>
            </default>
        </users>
        <quotas>
            <default>
                <interval>
                    <duration>3600</duration>
                    <queries>0</queries>
                    <errors>0</errors>
                    <result_rows>0</result_rows>
                    <read_rows>0</read_rows>
                    <execution_time>0</execution_time>
                </interval>
            </default>
        </quotas>
    </clickhouse>
    ```

    | 目录                                                       | 文件                                                                                                                                                                            |
    | -------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `fs/volumes/clickhouse-01/etc/clickhouse-server/users.d` | [`users.xml`](https://github.com/ClickHouse/examples/blob/main/docker-compose-recipes/recipes/cluster_1S_2R/fs/volumes/clickhouse-01/etc/clickhouse-server/users.d/users.xml) |
    | `fs/volumes/clickhouse-02/etc/clickhouse-server/users.d` | [`users.xml`](https://github.com/ClickHouse/examples/blob/main/docker-compose-recipes/recipes/cluster_1S_2R/fs/volumes/clickhouse-02/etc/clickhouse-server/users.d/users.xml) |

    在此示例中，为简便起见，default user 未设置密码。
    在实际环境中，不建议这样做。

    <Note>
      在此示例中，集群中所有节点上的 `users.xml` 文件都相同。
    </Note>
  </Step>

  <Step title="配置 ClickHouse Keeper" id="configure-clickhouse-keeper-nodes">
    ### Keeper 配置

    为了使复制正常工作，需要先搭建并配置 ClickHouse Keeper 集群。ClickHouse Keeper 为数据复制提供协调系统，
    可作为 ZooKeeper 的替代方案，当然也可以直接使用 ZooKeeper。
    不过，推荐使用 ClickHouse Keeper，因为它能提供更好的保障和
    可靠性，并且比 ZooKeeper 占用更少的资源。为了实现高可用性并
    保持 quorum，建议至少运行三个 ClickHouse Keeper 节点。

    <Note>
      ClickHouse Keeper 可以与 ClickHouse 一起运行在集群的任何节点上，不过
      更推荐将其部署在专用节点上，这样就可以独立于数据库集群对
      ClickHouse Keeper 集群进行扩缩容和管理。
    </Note>

    在示例文件夹的根目录下，使用以下命令为每个 ClickHouse Keeper 节点
    创建 `keeper_config.xml` 文件：

    ```bash theme={null}
    for i in {01..03}; do
      touch fs/volumes/clickhouse-keeper-${i}/etc/clickhouse-keeper/keeper_config.xml
    done
    ```

    修改在每个
    节点目录 `fs/volumes/clickhouse-keeper-{}/etc/clickhouse-keeper` 中创建的空配置文件。下方高亮显示的内容需要改为各节点对应的具体值：

    ```xml title="/clickhouse-keeper/keeper_config.xml" highlight={12} theme={null}
    <clickhouse replace="true">
        <logger>
            <level>information</level>
            <log>/var/log/clickhouse-keeper/clickhouse-keeper.log</log>
            <errorlog>/var/log/clickhouse-keeper/clickhouse-keeper.err.log</errorlog>
            <size>1000M</size>
            <count>3</count>
        </logger>
        <listen_host>0.0.0.0</listen_host>
        <keeper_server>
            <tcp_port>9181</tcp_port>
            <server_id>1</server_id>
            <log_storage_path>/var/lib/clickhouse/coordination/log</log_storage_path>
            <snapshot_storage_path>/var/lib/clickhouse/coordination/snapshots</snapshot_storage_path>
            <coordination_settings>
                <operation_timeout_ms>10000</operation_timeout_ms>
                <session_timeout_ms>30000</session_timeout_ms>
                <raft_logs_level>information</raft_logs_level>
            </coordination_settings>
            <raft_configuration>
                <server>
                    <id>1</id>
                    <hostname>clickhouse-keeper-01</hostname>
                    <port>9234</port>
                </server>
                <server>
                    <id>2</id>
                    <hostname>clickhouse-keeper-02</hostname>
                    <port>9234</port>
                </server>
                <server>
                    <id>3</id>
                    <hostname>clickhouse-keeper-03</hostname>
                    <port>9234</port>
                </server>
            </raft_configuration>
        </keeper_server>
    </clickhouse>
    ```

    | 目录                                                      | 文件                                                                                                                                                                                           |
    | ------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `fs/volumes/clickhouse-keeper-01/etc/clickhouse-keeper` | [`keeper_config.xml`](https://github.com/ClickHouse/examples/blob/main/docker-compose-recipes/recipes/cluster_1S_2R/fs/volumes/clickhouse-keeper-01/etc/clickhouse-keeper/keeper_config.xml) |
    | `fs/volumes/clickhouse-keeper-02/etc/clickhouse-keeper` | [`keeper_config.xml`](https://github.com/ClickHouse/examples/blob/main/docker-compose-recipes/recipes/cluster_1S_2R/fs/volumes/clickhouse-keeper-02/etc/clickhouse-keeper/keeper_config.xml) |
    | `fs/volumes/clickhouse-keeper-03/etc/clickhouse-keeper` | [`keeper_config.xml`](https://github.com/ClickHouse/examples/blob/main/docker-compose-recipes/recipes/cluster_1S_2R/fs/volumes/clickhouse-keeper-03/etc/clickhouse-keeper/keeper_config.xml) |

    每个配置文件都应包含以下唯一配置 (如下所示) 。
    所使用的 `server_id` 对于集群中的对应 ClickHouse Keeper 节点必须是唯一的，
    并且要与 `<raft_configuration>` 部分中定义的服务器 `<id>` 一致。
    `tcp_port` 是 ClickHouse Keeper *客户端* 使用的端口。

    ```xml theme={null}
    <tcp_port>9181</tcp_port>
    <server_id>{id}</server_id>
    ```

    以下部分用于配置参与 [Raft 共识算法](https://en.wikipedia.org/wiki/Raft_\(algorithm\)) 仲裁的服务器：

    ```xml highlight={6} theme={null}
    <raft_configuration>
        <server>
            <id>1</id>
            <hostname>clickhouse-keeper-01</hostname>
            <!-- ClickHouse Keeper 节点间通信使用的 TCP 端口 -->
            <port>9234</port>
        </server>
        <server>
            <id>2</id>
            <hostname>clickhouse-keeper-02</hostname>
            <port>9234</port>
        </server>
        <server>
            <id>3</id>
            <hostname>clickhouse-keeper-03</hostname>
            <port>9234</port>
        </server>
    </raft_configuration>
    ```

    <Tip>
      **ClickHouse Cloud 简化管理**

      [ClickHouse Cloud](/docs/zh/products/cloud/getting-started/intro)
      免去了管理分片和副本带来的运维负担。该
      平台会自动处理高可用性、复制和扩缩容。
      计算资源与存储相互分离，并可根据需求弹性扩展，无需手动
      配置或持续维护。

      [了解更多](/docs/zh/products/cloud/features/autoscaling/overview)
    </Tip>
  </Step>

  <Step title="测试设置" id="test-the-setup">
    请确保你的机器上已运行 Docker。
    在 `cluster_1S_2R` 目录的根目录下，使用 `docker-compose up` 命令启动集群：

    ```bash theme={null}
    docker-compose up -d
    ```

    你应该会看到 docker 开始拉取 ClickHouse 和 Keeper 镜像，
    然后启动容器：

    ```bash theme={null}
    [+] Running 6/6
     ✔ Network cluster_1s_2r_default   Created
     ✔ Container clickhouse-keeper-03  Started
     ✔ Container clickhouse-keeper-02  Started
     ✔ Container clickhouse-keeper-01  Started
     ✔ Container clickhouse-01         Started
     ✔ Container clickhouse-02         Started
    ```

    要确认集群是否正常运行，请连接到 `clickhouse-01` 或 `clickhouse-02` 之一，并运行
    以下查询。下面显示的是连接到第一个节点的命令：

    ```bash theme={null}
    # Connect to any node
    docker exec -it clickhouse-01 clickhouse-client
    ```

    如果一切顺利，您将看到 ClickHouse 客户端提示符：

    ```response theme={null}
    cluster_1S_2R node 1 :)
    ```

    运行以下查询，查看为哪些
    主机定义了哪些集群拓扑：

    ```sql title="Query" theme={null}
    SELECT 
        cluster,
        shard_num,
        replica_num,
        host_name,
        port
    FROM system.clusters;
    ```

    ```response title="Response" theme={null}
       ┌─cluster───────┬─shard_num─┬─replica_num─┬─host_name─────┬─port─┐
    1. │ cluster_1S_2R │         1 │           1 │ clickhouse-01 │ 9000 │
    2. │ cluster_1S_2R │         1 │           2 │ clickhouse-02 │ 9000 │
    3. │ default       │         1 │           1 │ localhost     │ 9000 │
       └───────────────┴───────────┴─────────────┴───────────────┴──────┘
    ```

    运行以下查询，检查 ClickHouse Keeper 集群的状态：

    ```sql title="Query" theme={null}
    SELECT *
    FROM system.zookeeper
    WHERE path IN ('/', '/clickhouse')
    ```

    ```response title="Response" theme={null}
       ┌─name───────┬─value─┬─path────────┐
    1. │ sessions   │       │ /clickhouse │
    2. │ task_queue │       │ /clickhouse │
    3. │ keeper     │       │ /           │
    4. │ clickhouse │       │ /           │
       └────────────┴───────┴─────────────┘
    ```

    `mntr` 命令也常用于验证 ClickHouse Keeper 是否正在运行，并获取三个 Keeper 节点之间关系的状态信息。
    在此示例使用的配置中，有三个节点协同工作。
    这些节点会选举出一个 leader，其余节点则为跟随者。

    `mntr` 命令会提供与性能相关的信息，以及特定节点是跟随者还是 leader。

    <Tip>
      你可能需要安装 `netcat`，才能将 `mntr` 命令发送给 Keeper。
      请参阅 [nmap.org](https://nmap.org/ncat/) 页面了解下载信息。
    </Tip>

    在 `clickhouse-keeper-01`、`clickhouse-keeper-02` 和
    `clickhouse-keeper-03` 的 shell 中运行以下命令，以检查每个 Keeper 节点的状态。下面显示的是
    `clickhouse-keeper-01` 的命令：

    ```bash theme={null}
    docker exec -it clickhouse-keeper-01  /bin/sh -c 'echo mntr | nc 127.0.0.1 9181'
    ```

    下面的响应展示了来自 follower 节点的示例响应：

    ```response title="Response" highlight={9} theme={null}
    zk_version      v23.3.1.2823-testing-46e85357ce2da2a99f56ee83a079e892d7ec3726
    zk_avg_latency  0
    zk_max_latency  0
    zk_min_latency  0
    zk_packets_received     0
    zk_packets_sent 0
    zk_num_alive_connections        0
    zk_outstanding_requests 0
    zk_server_state follower
    zk_znode_count  6
    zk_watch_count  0
    zk_ephemerals_count     0
    zk_approximate_data_size        1271
    zk_key_arena_size       4096
    zk_latest_snapshot_size 0
    zk_open_file_descriptor_count   46
    zk_max_file_descriptor_count    18446744073709551615
    ```

    下面的响应显示了 leader 节点返回的示例响应：

    ```response title="Response" highlight={9,18-19} theme={null}
    zk_version      v23.3.1.2823-testing-46e85357ce2da2a99f56ee83a079e892d7ec3726
    zk_avg_latency  0
    zk_max_latency  0
    zk_min_latency  0
    zk_packets_received     0
    zk_packets_sent 0
    zk_num_alive_connections        0
    zk_outstanding_requests 0
    zk_server_state leader
    zk_znode_count  6
    zk_watch_count  0
    zk_ephemerals_count     0
    zk_approximate_data_size        1271
    zk_key_arena_size       4096
    zk_latest_snapshot_size 0
    zk_open_file_descriptor_count   48
    zk_max_file_descriptor_count    18446744073709551615
    zk_followers    2
    zk_synced_followers     2
    ```

    至此，你已成功搭建了一个包含单个分片和两个副本的 ClickHouse 集群。
    下一步，你将在该集群中创建一个表。
  </Step>

  <Step title="创建数据库" id="creating-a-database">
    现在你已经确认集群已正确设置并正常运行，接下来你将重新创建一个与[英国房产价格](/docs/zh/get-started/sample-datasets/uk-price-paid)
    示例数据集教程中相同的表。该数据集包含自 1995 年以来英格兰和威尔士房地产成交价格的约 3000 万行
    数据。

    在单独的终端选项卡或窗口中分别运行以下命令，以连接到每个主机的客户端：

    ```bash theme={null}
    docker exec -it clickhouse-01 clickhouse-client
    docker exec -it clickhouse-02 clickhouse-client
    ```

    你可以从每台主机上的 `clickhouse-client` 运行下面的查询，以确认
    除默认数据库外，尚未创建任何数据库：

    ```sql title="Query" theme={null}
    SHOW DATABASES;
    ```

    ```response title="Response" theme={null}
       ┌─name───────────────┐
    1. │ INFORMATION_SCHEMA │
    2. │ default            │
    3. │ information_schema │
    4. │ system             │
       └────────────────────┘
    ```

    在 `clickhouse-01` 客户端中，使用
    `ON CLUSTER` 子句执行以下**分布式** DDL 查询，以创建一个名为 `uk` 的新数据库：

    ```sql highlight={2} theme={null}
    CREATE DATABASE IF NOT EXISTS uk 
    ON CLUSTER cluster_1S_2R;
    ```

    你可以再次从每台主机的客户端运行与之前相同的查询，
    以确认即使该查询仅在 `clickhouse-01` 上运行，
    该数据库也已在整个集群中创建：

    ```sql theme={null}
    SHOW DATABASES;
    ```

    ```response highlight={6} theme={null}
       ┌─name───────────────┐
    1. │ INFORMATION_SCHEMA │
    2. │ default            │
    3. │ information_schema │
    4. │ system             │
    5. │ uk                 │
       └────────────────────┘
    ```
  </Step>

  <Step title="在集群上创建表" id="creating-a-table">
    现在数据库已创建完成，接下来在集群上创建一个表。
    在任意一个主机客户端上运行以下查询：

    ```sql highlight={2,19} theme={null}
    CREATE TABLE IF NOT EXISTS uk.uk_price_paid_local
    ON CLUSTER cluster_1S_2R
    (
        price UInt32,
        date Date,
        postcode1 LowCardinality(String),
        postcode2 LowCardinality(String),
        type Enum8('terraced' = 1, 'semi-detached' = 2, 'detached' = 3, 'flat' = 4, 'other' = 0),
        is_new UInt8,
        duration Enum8('freehold' = 1, 'leasehold' = 2, 'unknown' = 0),
        addr1 String,
        addr2 String,
        street LowCardinality(String),
        locality LowCardinality(String),
        town LowCardinality(String),
        district LowCardinality(String),
        county LowCardinality(String)
    )
    ENGINE = ReplicatedMergeTree
    ORDER BY (postcode1, postcode2, addr1, addr2);
    ```

    请注意，除了添加了 `ON CLUSTER` 子句并使用 `ReplicatedMergeTree` 引擎之外，它与原始 `CREATE` 语句中使用的查询完全相同；该语句出自
    [UK property prices](/docs/zh/get-started/sample-datasets/uk-price-paid) 示例数据集教程。

    `ON CLUSTER` 子句用于分布式执行 `CREATE`、`DROP`、`ALTER` 和 `RENAME` 等 DDL (数据定义语言)
    查询，以确保这些
    schema 变更会应用到集群中的所有节点。

    [`ReplicatedMergeTree`](/docs/zh/reference/engines/table-engines/mergetree-family/replication#converting-from-mergetree-to-replicatedmergetree)
    引擎的工作方式与普通的 `MergeTree` 表引擎相同，但还会复制数据。

    你可以在 `clickhouse-01` 或 `clickhouse-02` 客户端中运行下面的查询，
    以确认该表已在整个集群中创建：

    ```sql title="Query" theme={null}
    SHOW TABLES IN uk;
    ```

    ```response title="Response" theme={null}
       ┌─name────────────────┐
    1. │ uk_price_paid.      │
       └─────────────────────┘
    ```
  </Step>

  <Step title="插入数据" id="inserting-data">
    由于数据集较大，完全摄取需要几分钟，我们将先插入一小部分数据。

    使用以下查询从 `clickhouse-01` 插入一个较小的数据子集：

    ```sql theme={null}
    INSERT INTO uk.uk_price_paid_local
    SELECT
        toUInt32(price_string) AS price,
        parseDateTimeBestEffortUS(time) AS date,
        splitByChar(' ', postcode)[1] AS postcode1,
        splitByChar(' ', postcode)[2] AS postcode2,
        transform(a, ['T', 'S', 'D', 'F', 'O'], ['terraced', 'semi-detached', 'detached', 'flat', 'other']) AS type,
        b = 'Y' AS is_new,
        transform(c, ['F', 'L', 'U'], ['freehold', 'leasehold', 'unknown']) AS duration,
        addr1,
        addr2,
        street,
        locality,
        town,
        district,
        county
    FROM url(
        'http://prod1.publicdata.landregistry.gov.uk.s3-website-eu-west-1.amazonaws.com/pp-complete.csv',
        'CSV',
        'uuid_string String,
        price_string String,
        time String,
        postcode String,
        a String,
        b String,
        c String,
        addr1 String,
        addr2 String,
        street String,
        locality String,
        town String,
        district String,
        county String,
        d String,
        e String'
    ) LIMIT 10000
    SETTINGS max_http_get_redirects=10;
    ```

    请注意，数据已在每台主机上完整地进行了复制：

    ```sql theme={null}
    -- clickhouse-01
    SELECT count(*)
    FROM uk.uk_price_paid_local

    --   ┌─count()─┐
    -- 1.│   10000 │
    --   └─────────┘

    -- clickhouse-02
    SELECT count(*)
    FROM uk.uk_price_paid_local

    --   ┌─count()─┐
    -- 1.│   10000 │
    --   └─────────┘
    ```

    为了演示某台主机发生故障时的情况，请在任意一台主机上创建一个简单的测试数据库和测试表：

    ```sql theme={null}
    CREATE DATABASE IF NOT EXISTS test ON CLUSTER cluster_1S_2R;
    CREATE TABLE test.test_table ON CLUSTER cluster_1S_2R
    (
        `id` UInt64,
        `name` String
    )
    ENGINE = ReplicatedMergeTree
    ORDER BY id;
    ```

    与 `uk_price_paid` 表一样，我们可以从任意主机插入数据：

    ```sql theme={null}
    INSERT INTO test.test_table (id, name) VALUES (1, 'Clicky McClickface');
    ```

    但如果其中一台主机宕机，会发生什么？要模拟这种情况，请执行以下命令停止
    `clickhouse-01`：

    ```bash theme={null}
    docker stop clickhouse-01
    ```

    运行以下命令确认主机已停止：

    ```bash theme={null}
    docker-compose ps
    ```

    ```response title="Response" theme={null}
    NAME                   IMAGE                                        COMMAND            SERVICE                CREATED          STATUS          PORTS
    clickhouse-02          clickhouse/clickhouse-server:latest          "/entrypoint.sh"   clickhouse-02          X minutes ago    Up X minutes    127.0.0.1:8124->8123/tcp, 127.0.0.1:9001->9000/tcp
    clickhouse-keeper-01   clickhouse/clickhouse-keeper:latest-alpine   "/entrypoint.sh"   clickhouse-keeper-01   X minutes ago    Up X minutes    127.0.0.1:9181->9181/tcp
    clickhouse-keeper-02   clickhouse/clickhouse-keeper:latest-alpine   "/entrypoint.sh"   clickhouse-keeper-02   X minutes ago    Up X minutes    127.0.0.1:9182->9181/tcp
    clickhouse-keeper-03   clickhouse/clickhouse-keeper:latest-alpine   "/entrypoint.sh"   clickhouse-keeper-03   X minutes ago    Up X minutes    127.0.0.1:9183->9181/tcp
    ```

    `clickhouse-01` 现已停机，向测试表中再插入一行数据并查询该表：

    ```sql theme={null}
    INSERT INTO test.test_table (id, name) VALUES (2, 'Alexey Milovidov');
    SELECT * FROM test.test_table;
    ```

    ```response title="Response" theme={null}
       ┌─id─┬─name───────────────┐
    1. │  1 │ Clicky McClickface │
    2. │  2 │ Alexey Milovidov   │
       └────┴────────────────────┘
    ```

    现在使用以下命令重启 `clickhouse-01` (之后可再次运行 `docker-compose ps` 进行确认) ：

    ```sql theme={null}
    docker start clickhouse-01
    ```

    运行 `docker exec -it clickhouse-01 clickhouse-client` 后，再次从 `clickhouse-01` 查询测试表：

    ```sql title="Query" theme={null}
    SELECT * FROM test.test_table
    ```

    ```response title="Response" theme={null}
       ┌─id─┬─name───────────────┐
    1. │  1 │ Clicky McClickface │
    2. │  2 │ Alexey Milovidov   │
       └────┴────────────────────┘
    ```

    如果您希望在此阶段摄取完整的英国房产价格数据集以便进行探索，可以执行以下查询：

    ```sql theme={null}
    TRUNCATE TABLE uk.uk_price_paid_local ON CLUSTER cluster_1S_2R;
    INSERT INTO uk.uk_price_paid_local
    SELECT
        toUInt32(price_string) AS price,
        parseDateTimeBestEffortUS(time) AS date,
        splitByChar(' ', postcode)[1] AS postcode1,
        splitByChar(' ', postcode)[2] AS postcode2,
        transform(a, ['T', 'S', 'D', 'F', 'O'], ['terraced', 'semi-detached', 'detached', 'flat', 'other']) AS type,
        b = 'Y' AS is_new,
        transform(c, ['F', 'L', 'U'], ['freehold', 'leasehold', 'unknown']) AS duration,
        addr1,
        addr2,
        street,
        locality,
        town,
        district,
        county
    FROM url(
        'http://prod1.publicdata.landregistry.gov.uk.s3-website-eu-west-1.amazonaws.com/pp-complete.csv',
        'CSV',
        'uuid_string String,
        price_string String,
        time String,
        postcode String,
        a String,
        b String,
        c String,
        addr1 String,
        addr2 String,
        street String,
        locality String,
        town String,
        district String,
        county String,
        d String,
        e String'
        ) SETTINGS max_http_get_redirects=10;
    ```

    从 `clickhouse-02` 或 `clickhouse-01` 查询该表：

    ```sql title="Query" theme={null}
    SELECT count(*) FROM uk.uk_price_paid_local;
    ```

    ```response title="Response" theme={null}
       ┌──count()─┐
    1. │ 30212555 │ -- 30.21 million
       └──────────┘
    ```
  </Step>
</Steps>

<div id="conclusion">
  ## 结论
</div>

这种集群拓扑的优势在于，使用两个副本后，
你的数据会分布在两台独立的主机上。如果其中一台主机发生故障，另一台副本
仍可继续提供数据服务，而不会发生任何数据丢失。这样就消除了存储层面的
单点故障。

当其中一台主机宕机时，剩余的副本仍然可以：

* 不间断地处理读查询
* 接受新的写入 (取决于你的一致性设置)
* 保持应用程序的服务可用性

当故障主机重新上线后，它可以：

* 自动从健康副本同步缺失的数据
* 无需人工干预即可恢复正常运行
* 快速恢复完整冗余

在下一个示例中，我们将介绍如何设置一个具有两个分片但
只有一个副本的集群。
