> ## 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.

# 复制 + 扩缩容

> 通过本教程，你将学习如何搭建一个简单的 ClickHouse 集群。

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 集群。
> 该集群由两个分片和两个副本组成，并配备一个 3 节点的 ClickHouse Keeper 集群，
> 用于负责协调管理并维持集群仲裁。

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

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

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

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

<div id="prerequisites">
  ## 前置条件
</div>

* 你此前已完成[本地 ClickHouse server](/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_2S_2R
    cd cluster_2S_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..04}; do
      mkdir -p fs/volumes/clickhouse-${i}/etc/clickhouse-server
    done
    ```

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

    ```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-03:
        image: "clickhouse/clickhouse-server:latest"
        user: "101:101"
        container_name: clickhouse-03
        hostname: clickhouse-03
        volumes:
          - ${PWD}/fs/volumes/clickhouse-03/etc/clickhouse-server/config.d/config.xml:/etc/clickhouse-server/config.d/config.xml
          - ${PWD}/fs/volumes/clickhouse-03/etc/clickhouse-server/users.d/users.xml:/etc/clickhouse-server/users.d/users.xml
        ports:
          - "127.0.0.1:8125:8123"
          - "127.0.0.1:9002:9000"
        depends_on:
          - clickhouse-keeper-01
          - clickhouse-keeper-02
          - clickhouse-keeper-03
      clickhouse-04:
        image: "clickhouse/clickhouse-server:latest"
        user: "101:101"
        container_name: clickhouse-04
        hostname: clickhouse-04
        volumes:
          - ${PWD}/fs/volumes/clickhouse-04/etc/clickhouse-server/config.d/config.xml:/etc/clickhouse-server/config.d/config.xml
          - ${PWD}/fs/volumes/clickhouse-04/etc/clickhouse-server/users.d/users.xml:/etc/clickhouse-server/users.d/users.xml
        ports:
          - "127.0.0.1:8126:8123"
          - "127.0.0.1:9003: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..04}; 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,64-67} 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_2S_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_2S_2R>
                <shard>
                    <internal_replication>true</internal_replication>
                    <replica>
                        <host>clickhouse-01</host>
                        <port>9000</port>
                    </replica>
                    <replica>
                        <host>clickhouse-03</host>
                        <port>9000</port>
                    </replica>
                </shard>
                <shard>
                    <internal_replication>true</internal_replication>
                    <replica>
                        <host>clickhouse-02</host>
                        <port>9000</port>
                    </replica>
                    <replica>
                        <host>clickhouse-04</host>
                        <port>9000</port>
                    </replica>
                </shard>
            </cluster_2S_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>
        </macros>
    </clickhouse>
    ```

    | 目录                                                        | File 表引擎                                                                                                                                                                         |
    | --------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `fs/volumes/clickhouse-01/etc/clickhouse-server/config.d` | [`config.xml`](https://github.com/ClickHouse/examples/blob/main/docker-compose-recipes/recipes/cluster_2S_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_2S_2R/fs/volumes/clickhouse-02/etc/clickhouse-server/config.d/config.xml) |
    | `fs/volumes/clickhouse-03/etc/clickhouse-server/config.d` | [`config.xml`](https://github.com/ClickHouse/examples/blob/main/docker-compose-recipes/recipes/cluster_2S_2R/fs/volumes/clickhouse-03/etc/clickhouse-server/config.d/config.xml) |
    | `fs/volumes/clickhouse-04/etc/clickhouse-server/config.d` | [`config.xml`](https://github.com/ClickHouse/examples/blob/main/docker-compose-recipes/recipes/cluster_2S_2R/fs/volumes/clickhouse-04/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_2S_2R` 在此处定义。

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

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

    ```xml theme={null}
    <remote_servers>
       <!-- cluster name (should not contain dots) -->
      <cluster_2S_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-03</host>
                  <port>9000</port>
              </replica>
          </shard>
          <shard>
              <internal_replication>true</internal_replication>
              <replica>
                  <host>clickhouse-02</host>
                  <port>9000</port>
              </replica>
              <replica>
                  <host>clickhouse-04</host>
                  <port>9000</port>
              </replica>
          </shard>
      </cluster_2S_2R>
    </remote_servers>
    ```

    `<cluster_2S_2R></cluster_2S_2R>` 部分定义了集群的结构，并作为分布式 DDL 查询的模板，这些查询通过 `ON CLUSTER` 子句在整个集群中执行。

    #### 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>
    </macros>
    ```

    ### 用户配置

    现在，将以下内容写入位于 `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>
    ```

    在此示例中，为简便起见，默认用户未设置密码。
    实际生产环境中，不建议采用此方式。

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

  <Step title="配置 ClickHouse Keeper" id="configure-clickhouse-keeper-nodes">
    接下来，您将配置用于协调的 ClickHouse Keeper。

    ### 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_2S_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_2S_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_2S_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_2S_2R` 目录根目录下，使用 `docker-compose up` 命令启动集群：

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

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

    ```bash theme={null}
    [+] Running 8/8
     ✔ Network   cluster_2s_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
     ✔ Container clickhouse-04             Started
     ✔ Container clickhouse-03             Started
    ```

    要验证集群是否正在运行，请连接到任意一个节点并运行
    以下查询。下面以连接到第一个节点的命令为例：

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

    如果成功，你将看到 ClickHouse 客户端的提示符：

    ```response theme={null}
    cluster_2S_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_2S_2R │         1 │           1 │ clickhouse-01 │ 9000 │
    2. │ cluster_2S_2R │         1 │           2 │ clickhouse-03 │ 9000 │
    3. │ cluster_2S_2R │         2 │           1 │ clickhouse-02 │ 9000 │
    4. │ cluster_2S_2R │         2 │           2 │ clickhouse-04 │ 9000 │
    5. │ 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. │ task_queue │       │ /clickhouse │
    2. │ sessions   │       │ /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">
    现在你已经确认 cluster 已正确配置并正常运行，接下来你将重新创建与 [英国房产价格](/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
    docker exec -it clickhouse-03 clickhouse-client
    docker exec -it clickhouse-04 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_2S_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_2S_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('/clickhouse/tables/{database}/{table}/{shard}', '{replica}')
    ORDER BY (postcode1, postcode2, addr1, addr2);
    ```

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

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

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

    * `zoo_path`：表元数据的 Keeper/ZooKeeper 路径。
    * `replica_name`：表的副本名称。

    <br />

    `zoo_path` 参数可以设置为你选择的任意值，不过建议遵循
    使用前缀的约定

    ```text theme={null}
    /clickhouse/tables/{shard}/{database}/{table}
    ```

    其中：

    * `{database}` 和 `{table}` 会自动替换。
    * `{shard}` 和 `{replica}` 是宏，此前已在每个 ClickHouse 节点的 `config.xml` 文件中[定义](#macros-config-explanation)。

    你可以在每台主机的客户端中运行以下查询，以确认该表已在整个集群中创建：

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

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

  <Step title="将数据插入分布式表" id="inserting-data-using-distributed">
    要向该表插入数据，不能使用 `ON CLUSTER`，因为它不适用于 `INSERT`、`UPDATE` 和 `DELETE` 这类 DML (数据操作语言) 查询。要插入数据，必须使用
    [`Distributed`](/docs/zh/reference/engines/table-engines/special/distributed) 表引擎。
    正如你在搭建包含 2 个分片和 1 个副本的集群[指南](/docs/zh/guides/oss/deployment-and-scaling/examples/2-shards-1-replica)中所了解的，分布式表是可访问位于不同
    主机上分片的表，并使用 `Distributed` 表引擎定义。
    分布式表充当集群中所有分片之间的接口。

    在任意主机的客户端上，运行以下查询，基于我们在上一步创建的现有副本表来创建一个分布式表：

    ```sql theme={null}
    CREATE TABLE IF NOT EXISTS uk.uk_price_paid_distributed
    ON CLUSTER cluster_2S_2R
    ENGINE = Distributed('cluster_2S_2R', 'uk', 'uk_price_paid_local', rand());
    ```

    现在，在每台主机上，你都会在 `uk` 数据库中看到以下表：

    ```response theme={null}
       ┌─name──────────────────────┐
    1. │ uk_price_paid_distributed │
    2. │ uk_price_paid_local       │
       └───────────────────────────┘
    ```

    可以通过以下查询，从任意主机客户端向 `uk_price_paid_distributed` 表插入数据：

    ```sql theme={null}
    INSERT INTO uk.uk_price_paid_distributed
    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;
    ```

    运行以下查询，确认已插入的数据是否已均匀分布在集群的各个节点上：

    ```sql theme={null}
    SELECT count(*)
    FROM uk.uk_price_paid_distributed;

    SELECT count(*) FROM uk.uk_price_paid_local;
    ```

    ```response theme={null}
       ┌──count()─┐
    1. │ 30212555 │ -- 30.21 million
       └──────────┘

       ┌──count()─┐
    1. │ 15105983 │ -- 15.11 million
       └──────────┘
    ```
  </Step>
</Steps>

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

这种包含 2 个分片和 2 个副本的集群拓扑的优势在于，它同时具备可扩展性和容错性。
数据分布在不同的主机上，降低了每个节点的存储和 I/O 需求；同时，查询会在两个分片上并行处理，从而提升性能并提高内存使用效率。
更重要的是，该集群即使丢失一个节点，也能继续不中断地响应查询，因为每个分片在另一节点上都有一个备用副本。

这种集群拓扑的主要缺点是存储开销更高——由于每个分片都要复制一份数据，因此与没有副本的配置相比，它需要两倍的存储容量。
此外，虽然集群能够承受单个节点故障，但如果同时丢失两个节点，集群可能无法正常工作，具体取决于故障的是哪些节点，以及分片是如何分布的。
这种拓扑在可用性和成本之间取得了平衡，因此适用于需要一定容错性、但又不想承担更高复制因子成本的生产环境。

要了解 ClickHouse Cloud 如何处理查询，并同时提供可扩展性和容错性，请参阅["并行副本"](/docs/zh/products/cloud/features/infrastructure/parallel-replicas)一节。
