> ## 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/sharding.webp?fit=max&auto=format&n=NvnCM4vX9aZ07JxK&q=85&s=bb9b9d42024b0aed234f3cc36f854628" size="md" alt="2 个分片和 1 个副本的架构图" width="1200" height="800" data-path="images/deployment-guides/replication-sharding-examples/sharding.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_2S_1R
    cd cluster_2S_1R

    # 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_2S_1R` 目录中：

    ```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
        networks:
          cluster_2S_1R:
            ipv4_address: 192.168.7.1
        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
        networks:
          cluster_2S_1R:
            ipv4_address: 192.168.7.2
        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
        networks:
          cluster_2S_1R:
            ipv4_address: 192.168.7.5
        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
        networks:
          cluster_2S_1R:
            ipv4_address: 192.168.7.6
        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
        networks:
          cluster_2S_1R:
            ipv4_address: 192.168.7.7
        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"
    networks:
      cluster_2S_1R:
        driver: bridge
        ipam:
          config:
            - subnet: 192.168.7.0/24
              gateway: 192.168.7.254
    ```

    创建以下子目录和文件：

    ```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,54-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_2S_1R 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_1R>
                <shard>
                    <replica>
                        <host>clickhouse-01</host>
                        <port>9000</port>
                    </replica>
                </shard>
                <shard>
                    <replica>
                        <host>clickhouse-02</host>
                        <port>9000</port>
                    </replica>
                </shard>
            </cluster_2S_1R>
        </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_1R/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_1R/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_2S_1R` 即在此处定义。

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

    `internal_replication` 默认保持为 false，因为每个分片只有一个副本。

    ```xml theme={null}
    <remote_servers>
        <cluster_2S_1R>
            <shard>
                <replica>
                    <host>clickhouse-01</host>
                    <port>9000</port>
                </replica>
            </shard>
            <shard>
                <replica>
                    <host>clickhouse-02</host>
                    <port>9000</port>
                </replica>
            </shard>
        </cluster_2S_1R>
    </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>
    </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>
    ```

    | 目录                                                       | File 表引擎                                                                                                                                                                      |
    | -------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `fs/volumes/clickhouse-01/etc/clickhouse-server/users.d` | [`users.xml`](https://github.com/ClickHouse/examples/blob/main/docker-compose-recipes/recipes/cluster_2S_1R/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_2S_1R/fs/volumes/clickhouse-02/etc/clickhouse-server/users.d/users.xml) |

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

    <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_2S_1R/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_1R/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_1R/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_1R` 目录的根目录中，使用 `docker-compose up` 命令启动集群：

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

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

    ```bash theme={null}
    [+] Running 6/6
     ✔ Network cluster_2s_1r_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_2S_1R 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_1R │         1 │           1 │ clickhouse-01 │ 9000 │
    2. │ cluster_2S_1R │         2 │           1 │ 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. │ task_queue │       │ /clickhouse │
    2. │ sessions   │       │ /clickhouse │
    3. │ clickhouse │       │ /           │
    4. │ keeper     │       │ /           │
       └────────────┴───────┴─────────────┘
    ```

    `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
    ```

    你可以在每台主机的 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_1R;
    ```

    你可以再次在每台主机的客户端中运行与之前相同的查询，
    以确认虽然该查询仅在 `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} theme={null}
    CREATE TABLE IF NOT EXISTS uk.uk_price_paid_local
    ON CLUSTER cluster_2S_1R
    (
        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 = MergeTree
    ORDER BY (postcode1, postcode2, addr1, addr2);
    ```

    请注意，它与 [UK property prices](/docs/zh/get-started/sample-datasets/uk-price-paid) 示例 dataset 教程中原始 `CREATE` statement 使用的查询完全相同，
    唯一的区别是 `ON CLUSTER` clause。

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

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

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

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

    在插入英国房价成交数据之前，我们先做个简单实验，看看从任一主机向普通表插入数据时会发生什么。

    从任一主机执行以下查询，创建一个测试数据库和表：

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

    现在，在 `clickhouse-01` 上运行以下 `INSERT` 查询：

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

    切换到 `clickhouse-02`，然后运行以下 `INSERT` 查询：

    ```sql title="Query" theme={null}
    INSERT INTO test.test_table (id, name) VALUES (1, 'Alexey Milovidov');
    ```

    现在在 `clickhouse-01` 或 `clickhouse-02` 上执行以下查询：

    ```sql theme={null}
    -- from clickhouse-01
    SELECT * FROM test.test_table;
    --   ┌─id─┬─name───────────────┐
    -- 1.│  1 │ Clicky McClickface │
    --   └────┴────────────────────┘

    --from clickhouse-02
    SELECT * FROM test.test_table;
    --   ┌─id─┬─name───────────────┐
    -- 1.│  1 │ Alexey Milovidov   │
    --   └────┴────────────────────┘
    ```

    你会注意到，与 `ReplicatedMergeTree` 表不同，这里返回的只有插入到该特定主机上的那一行，
    而不是两行都返回。

    要读取跨两个分片的数据，我们需要一种能够处理跨所有分片查询的接口：
    当我们对它运行 select 查询时，它会合并两个分片中的数据；
    而当我们运行 insert 查询时，它会将数据插入到两个分片中。

    在 ClickHouse 中，这种接口称为**分布式表**，我们使用
    [`Distributed`](/docs/zh/reference/engines/table-engines/special/distributed) 表引擎来创建它。下面来看看它的工作方式。
  </Step>

  <Step title="创建分布式表" id="create-distributed-table">
    使用以下查询创建一个分布式表：

    ```sql theme={null}
    CREATE TABLE test.test_table_dist ON CLUSTER cluster_2S_1R AS test.test_table
    ENGINE = Distributed('cluster_2S_1R', 'test', 'test_table', rand())
    ```

    在这个示例中，选择 `rand()` 函数作为分片键，这样
    插入的数据就会随机分布到各个分片上。

    现在，从任一主机查询该分布式表，返回的都会是
    分别插入到两台主机上的两行数据，这与前一个示例不同：

    ```sql theme={null}
    SELECT * FROM test.test_table_dist;
    ```

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

    我们也对英国房价数据执行同样的操作。在任意一个主机客户端上，
    运行以下查询，基于我们之前使用 `ON CLUSTER` 创建的现有表
    创建一个分布式表：

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

  <Step title="向分布式表插入数据" id="inserting-data-into-distributed-table">
    现在连接到任意一台主机并插入数据：

    ```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 title="Query" theme={null}
    SELECT count(*)
    FROM uk.uk_price_paid_distributed
    ```

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

    在任意一台主机上运行以下查询，您将看到数据已大致均匀地分布在各个分片上 (请注意，写入哪个分片是由 `rand()` 决定的，因此您的结果可能与此不同) ：

    ```sql theme={null}
    -- from clickhouse-01
    SELECT count(*)
    FROM uk.uk_price_paid_local
    --    ┌──count()─┐
    -- 1. │ 15107353 │ -- 15.11 million
    --    └──────────┘

    --from clickhouse-02
    SELECT count(*)
    FROM uk.uk_price_paid_local
    --    ┌──count()─┐
    -- 1. │ 15105202 │ -- 15.11 million
    --    └──────────┘
    ```

    如果其中一台主机发生故障，会发生什么？我们通过关闭 `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-02` 上对 Distributed 表执行与之前相同的 select 查询：

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

    ```response title="Response" highlight={6} theme={null}
    Received exception from server (version 25.5.2):
    Code: 279. DB::Exception: Received from localhost:9000. DB::Exception: All connection tries failed. Log:

    Code: 32. DB::Exception: Attempt to read after eof. (ATTEMPT_TO_READ_AFTER_EOF) (version 25.5.2.47 (official build))
    Code: 209. DB::NetException: Timeout: connect timed out: 192.168.7.1:9000 (clickhouse-01:9000, 192.168.7.1, local address: 192.168.7.2:37484, connection timeout 1000 ms). (SOCKET_TIMEOUT) (version 25.5.2.47 (official build))
    Code: 198. DB::NetException: Not found address of host: clickhouse-01: (clickhouse-01:9000, 192.168.7.1, local address: 192.168.7.2:37484). (DNS_ERROR) (version 25.5.2.47 (official build))

    : While executing Remote. (ALL_CONNECTION_TRIES_FAILED)
    ```

    遗憾的是，我们的集群并不具备容错能力。一旦某个主机发生故障，集群将被视为不健康状态，查询也会随之失败。这与我们在[上一个示例](/docs/zh/guides/oss/deployment-and-scaling/examples/1-shard-2-replicas)中看到的副本表有所不同——在那个示例中，即使某个主机发生故障，我们仍然能够正常写入数据。
  </Step>
</Steps>

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

这种集群拓扑的优势在于，数据会分布到不同的主机上，
并且每个节点只需要一半的存储空间。更重要的是，查询
会在两个分片上处理，这样能更高效地利用内存，
并减少每台主机上的 I/O。

这种集群拓扑的主要缺点当然是，一旦失去其中一台
主机，我们就无法再提供查询服务。

在[下一个示例](/docs/zh/guides/oss/deployment-and-scaling/examples/2-shards-2-replicas)中，我们将介绍如何
搭建一个包含两个分片和两个副本的集群，从而同时实现可扩展性和
容错能力。
