Skip to content
ClickHouse Docs
ClickHouse DocsClickHouse Docs

스케일링

이 예시에서는 확장 가능한 간단한 ClickHouse 클러스터를 설정하는 방법을 알아봅니다. 5대의 서버가 구성됩니다. 이 중 2대는 데이터를 세그먼트로 분산하는 데 사용됩니다. 나머지 3대의 서버는 클러스터 조정을 위해 사용됩니다.

설정할 클러스터의 아키텍처는 아래와 같습니다:

2개의 세그먼트와 1개의 레플리카를 위한 아키텍처 다이어그램

사전 요구 사항

  • 이전에 로컬 ClickHouse 서버를 설정한 적이 있습니다
  • 설정 파일 등 ClickHouse의 기본 구성 개념을 이해하고 있습니다
  • 사용 중인 환경에 Docker가 설치되어 있습니다

디렉터리 구조 및 테스트 환경 설정

이 튜토리얼에서는 Docker compose를 사용하여 ClickHouse 클러스터를 구성합니다. 이 설정은 별도의 로컬 머신, 가상 머신 또는 클라우드 인스턴스 환경에서도 동작하도록 수정할 수 있습니다.

다음 명령어를 실행하여 이 예시에 필요한 디렉터리 구조를 설정하십시오.

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 디렉터리에 추가하십시오:

docker-compose.ymlyaml
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

다음 하위 디렉터리와 파일을 생성하십시오.

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 서버 설정 파일 config.xml이 있으며, 여기에서 각 ClickHouse 노드의 사용자 지정 구성을 정의합니다. 이 구성은 모든 ClickHouse 설치에 함께 제공되는 기본 config.xml ClickHouse 설정 파일과 결합됩니다.
  • users.d 디렉터리에는 사용자 설정 파일 users.xml이 있으며, 여기에서 사용자별 사용자 지정 구성을 정의합니다. 이 구성은 모든 ClickHouse 설치에 함께 제공되는 기본 ClickHouse users.xml 설정 파일과 결합됩니다.

ClickHouse 노드 구성

서버 설정

이제 fs/volumes/clickhouse-{}/etc/clickhouse-server/config.d에 있는 각 빈 설정 파일 config.xml을 수정하십시오. 아래에 강조된 줄은 각 노드에 맞게 변경해야 합니다:

<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
fs/volumes/clickhouse-02/etc/clickhouse-server/config.d config.xml

위 설정 파일의 각 섹션에 대해서는 아래에서 자세히 설명합니다.

네트워킹 및 로깅

listen host 설정을 활성화하면 네트워크 인터페이스에 대한 외부 통신이 허용됩니다. 이렇게 하면 다른 호스트에서 ClickHouse 서버 호스트에 접근할 수 있습니다:

<listen_host>0.0.0.0</listen_host>

HTTP API 포트는 8123으로 설정됩니다:

<http_port>8123</http_port>

clickhouse-client 및 기타 네이티브 ClickHouse 도구와 clickhouse-server 및 기타 clickhouse-servers 간에 ClickHouse 네이티브 프로토콜로 통신하는 데 사용하는 TCP 포트는 9000으로 설정됩니다:

<tcp_port>9000</tcp_port>

로깅은 <logger> 블록에서 정의됩니다. 다음 예시 구성은 1000M 크기에서 최대 3회 롤오버되는 디버그 로그를 설정합니다:

<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 설정 파일에 포함된 주석을 참조하십시오.

클러스터 구성

클러스터 구성은 <remote_servers> 블록에서 설정합니다. 여기서 클러스터 이름 cluster_2S_1R을 정의합니다.

<cluster_2S_1R></cluster_2S_1R> 블록은 <shard></shard><replica></replica> 설정을 사용하여 클러스터의 레이아웃을 정의하며, ON CLUSTER 절을 통해 클러스터 전체에서 실행되는 분산 DDL 쿼리의 템플릿 역할을 합니다. 기본적으로 분산 DDL 쿼리는 허용되지만, allow_distributed_ddl_queries 설정으로 비활성화할 수도 있습니다.

세그먼트당 레플리카가 하나뿐이므로 internal_replication은 기본값 false로 유지됩니다.

<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 Keeper(또는 ZooKeeper)가 실행 중인 위치를 ClickHouse에 알려줍니다. ClickHouse Keeper 클러스터를 사용하는 경우, 클러스터의 각 <node>에 대해 <host><port> 태그를 사용하여 호스트명과 포트 번호를 지정해야 합니다.

ClickHouse Keeper 설정은 튜토리얼의 다음 단계에서 설명합니다.

<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> 섹션은 복제된 테이블에 대한 매개변수 치환을 정의하는 데 사용됩니다. 이 값들은 system.macros에 나열되며, 쿼리에서 {shard}{replica} 같은 치환 값을 사용할 수 있습니다.

<macros>
    <shard>01</shard>
    <replica>01</replica>
</macros>

사용자 구성

이제 fs/volumes/clickhouse-{}/etc/clickhouse-server/users.d에 위치한 빈 설정 파일 users.xml을 각각 아래 내용으로 수정하십시오.

/users.d/users.xmlxml
<?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
fs/volumes/clickhouse-02/etc/clickhouse-server/users.d users.xml

이 예시에서는 편의상 기본 사용자를 비밀번호 없이 구성합니다. 실제 운영 환경에서는 권장하지 않는 방식입니다.

ClickHouse Keeper 구성

Keeper 설정

복제가 작동하려면 ClickHouse Keeper 클러스터를 설정하고 구성해야 합니다. ClickHouse Keeper는 데이터 복제를 위한 조정 시스템을 제공하며, 대체 구성 요소로 Zookeeper를 사용할 수도 있습니다. 하지만 ClickHouse Keeper는 ZooKeeper보다 더 나은 보장과 신뢰성을 제공하고 리소스도 더 적게 사용하므로 권장됩니다. 고가용성을 확보하고 쿼럼을 유지하려면 최소 3개의 ClickHouse Keeper 노드를 실행하는 것이 좋습니다.

다음 명령을 사용하여 예시 폴더의 루트에서 각 ClickHouse Keeper 노드용 keeper_config.xml 파일을 생성하세요:

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에 생성된 빈 설정 파일을 수정합니다. 아래에서 강조 표시된 줄은 각 노드별로 맞게 변경해야 합니다:

/clickhouse-keeper/keeper_config.xmlxml
<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
fs/volumes/clickhouse-keeper-02/etc/clickhouse-keeper keeper_config.xml
fs/volumes/clickhouse-keeper-03/etc/clickhouse-keeper keeper_config.xml

각 설정 파일에는 다음과 같은 고유한 구성이 포함되어야 합니다(아래 참조). 사용할 server_id는 cluster 내 해당 ClickHouse Keeper 노드에서 고유해야 하며, <raft_configuration> 섹션에 정의된 서버 <id>와 일치해야 합니다. tcp_port는 ClickHouse Keeper의 클라이언트가 사용하는 포트입니다.

<tcp_port>9181</tcp_port>
<server_id>{id}</server_id>

다음 섹션은 Raft 합의 알고리즘의 쿼럼에 참여하는 서버를 구성하는 데 사용됩니다:

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

설정 확인

로컬 머신에서 Docker가 실행 중인지 확인하십시오. cluster_2S_1R 디렉터리의 루트에서 docker-compose up 명령으로 클러스터를 시작하십시오:

docker-compose up -d

docker가 ClickHouse 및 Keeper 이미지를 가져오기 시작하고, 이어서 컨테이너를 시작하는 것을 볼 수 있습니다:

[+] 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 중 하나에 연결한 다음 아래 쿼리를 실행하십시오. 첫 번째 노드에 연결하는 명령은 다음과 같습니다:

# Connect to any node
docker exec -it clickhouse-01 clickhouse-client

성공하면 ClickHouse client 프롬프트가 표시됩니다:

cluster_2S_1R node 1 :)

다음 쿼리를 실행하여 각 호스트에 어떤 클러스터 토폴로지가 정의되어 있는지 확인하십시오:

Querysql
SELECT 
    cluster,
    shard_num,
    replica_num,
    host_name,
    port
FROM system.clusters;
Responseresponse
   ┌─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 클러스터의 상태를 확인하세요:

Querysql
SELECT *
FROM system.zookeeper
WHERE path IN ('/', '/clickhouse')
Responseresponse
   ┌─name───────┬─value─┬─path────────┐
1. │ task_queue │       │ /clickhouse │
2. │ sessions   │       │ /clickhouse │
3. │ clickhouse │       │ /           │
4. │ keeper     │       │ /           │
   └────────────┴───────┴─────────────┘

mntr 명령은 ClickHouse Keeper가 실행 중인지 확인하고, 3개의 Keeper 노드가 서로 어떤 상태인지에 대한 정보를 확인하는 데에도 일반적으로 사용됩니다. 이 예시에서 사용된 구성에서는 3개의 노드가 함께 동작합니다. 노드들은 리더를 선출하고, 나머지 노드들은 팔로워가 됩니다.

mntr 명령은 성능 관련 정보와 함께 특정 노드가 팔로워인지 리더인지에 대한 정보도 제공합니다.

각 Keeper 노드의 상태를 확인하려면 clickhouse-keeper-01, clickhouse-keeper-02, clickhouse-keeper-03의 셸에서 아래 명령을 실행하십시오. clickhouse-keeper-01용 명령은 아래와 같습니다:

docker exec -it clickhouse-keeper-01  /bin/sh -c 'echo mntr | nc 127.0.0.1 9181'

아래는 팔로워 노드에서 반환된 응답의 예시입니다:

Responseresponse
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 노드의 응답 예시입니다:

Responseresponse
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

이로써 2개의 세그먼트와 각 세그먼트당 1개의 레플리카로 구성된 ClickHouse 클러스터를 성공적으로 설정했습니다. 다음 단계에서는 클러스터에 테이블을 생성합니다.

데이터베이스 생성

이제 클러스터가 올바르게 설정되어 실행 중임을 확인했으므로, 영국 부동산 가격 예시 데이터셋 튜토리얼에서 사용한 것과 동일한 테이블을 다시 생성합니다. 이 데이터셋은 1995년 이후 잉글랜드와 웨일스의 부동산 거래 가격 데이터 약 3천만 행으로 구성됩니다.

각 호스트의 클라이언트에 연결하려면, 별도의 터미널 탭 또는 창에서 다음 각 명령을 실행하십시오:

docker exec -it clickhouse-01 clickhouse-client
docker exec -it clickhouse-02 clickhouse-client

각 호스트의 clickhouse-client에서 아래 쿼리를 실행하여 기본 데이터베이스 외에는 아직 생성된 데이터베이스가 없는지 확인할 수 있습니다:

Querysql
SHOW DATABASES;
Responseresponse
   ┌─name───────────────┐
1. │ INFORMATION_SCHEMA │
2. │ default            │
3. │ information_schema │
4. │ system             │
   └────────────────────┘

clickhouse-01 클라이언트에서 ON CLUSTER 절을 사용해 uk라는 새 데이터베이스를 생성하는 다음 분산 DDL 쿼리를 실행하십시오:

CREATE DATABASE IF NOT EXISTS uk 
ON CLUSTER cluster_2S_1R;

각 호스트의 클라이언트에서 이전과 동일한 쿼리를 다시 실행하여, 쿼리를 clickhouse-01에서만 실행했더라도 데이터베이스가 클러스터 전체에 생성되었는지 확인할 수 있습니다:

SHOW DATABASES;
   ┌─name───────────────┐
1. │ INFORMATION_SCHEMA │
2. │ default            │
3. │ information_schema │
4. │ system             │
5. │ uk                 │
   └────────────────────┘

클러스터에 테이블 생성

이제 데이터베이스가 생성되었으므로 테이블(table)을 생성합니다. 호스트 클라이언트 어느 곳에서든 다음 쿼리를 실행하세요:

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);

원래 CREATE statement에서 사용된 쿼리와 UK property prices 예시 데이터셋 튜토리얼의 쿼리는 동일하며, 차이점은 ON CLUSTER 절뿐입니다.

ON CLUSTER 절은 CREATE, DROP, ALTER, RENAME과 같은 DDL (Data Definition Language) 쿼리를 분산 실행하도록 설계되었으며, 이러한 스키마 변경이 cluster의 모든 노드에 적용되도록 보장합니다.

아래 쿼리를 각 호스트의 클라이언트에서 실행하여 테이블이 cluster 전체에 생성되었는지 확인할 수 있습니다:

Querysql
SHOW TABLES IN uk;
Responseresponse
   ┌─name────────────────┐
1. │ uk_price_paid_local │
   └─────────────────────┘

영국 부동산 실거래가 데이터를 삽입하기 전에, 어느 호스트에서 일반 테이블에 데이터를 삽입하면 어떤 일이 일어나는지 간단한 실험으로 확인해 보겠습니다.

어느 호스트에서든 다음 쿼리를 실행하여 테스트 데이터베이스와 테이블을 생성하십시오:

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 쿼리를 실행하세요:

INSERT INTO test.test_table (id, name) VALUES (1, 'Clicky McClickface');

clickhouse-02로 전환한 후 다음 INSERT 쿼리를 실행하세요:

Querysql
INSERT INTO test.test_table (id, name) VALUES (1, 'Alexey Milovidov');

이제 clickhouse-01 또는 clickhouse-02에서 다음 쿼리를 실행하십시오:

-- 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 테이블 엔진을 사용해 생성합니다. 어떻게 작동하는지 살펴보겠습니다.

분산 테이블 생성

다음 쿼리를 사용하여 분산 테이블을 생성하세요:

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() 함수를 세그먼트 분할 키로 선택하여 삽입되는 데이터가 세그먼트 전반에 무작위로 분산되도록 합니다.

이제 어느 호스트에서든 분산 테이블을 쿼리하면 이전 예시와는 달리 두 호스트에 삽입된 두 행이 모두 반환됩니다:

SELECT * FROM test.test_table_dist;
   ┌─id─┬─name───────────────┐
1. │  1 │ Alexey Milovidov   │
2. │  1 │ Clicky McClickface │
   └────┴────────────────────┘

영국 부동산 가격 데이터에도 동일하게 적용하겠습니다. 어느 호스트 클라이언트에서든 다음 쿼리를 실행하여, 앞서 ON CLUSTER로 생성한 기존 테이블을 사용해 분산 테이블을 생성하십시오:

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());

분산 테이블에 데이터 삽입

이제 호스트 중 하나에 연결하여 데이터를 삽입하십시오:

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;

데이터가 삽입되면 분산 테이블을 사용하여 행 수를 확인할 수 있습니다:

Querysql
SELECT count(*)
FROM uk.uk_price_paid_distributed
Responseresponse
   ┌──count()─┐
1. │ 30212555 │ -- 30.21 million
   └──────────┘

두 호스트 중 어느 쪽에서든 다음 쿼리를 실행하면 데이터가 세그먼트 전반에 걸쳐 거의 균등하게 분산되어 있음을 확인할 수 있습니다(어느 세그먼트에 삽입할지는 rand()로 결정되므로 실행 결과는 다를 수 있습니다):

-- 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을 종료하여 이 상황을 시뮬레이션해 보겠습니다:

docker stop clickhouse-01

다음 명령을 실행하여 호스트가 중단되었는지 확인하십시오:

docker-compose ps
Responseresponse
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에서 앞서 분산 테이블에 실행했던 동일한 select 쿼리를 실행하십시오:

SELECT count(*)
FROM uk.uk_price_paid_distributed
Responseresponse
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)

안타깝게도 현재 클러스터는 내결함성(fault-tolerant)을 갖추고 있지 않습니다. 호스트 중 하나에 장애가 발생하면 클러스터가 비정상 상태로 간주되어 쿼리가 실패합니다. 이전 예시에서 살펴본 복제된 테이블(Replicated Table)은 호스트 중 하나에 장애가 발생하더라도 데이터를 삽입할 수 있었던 것과는 대조적입니다.

결론

이 클러스터 토폴로지의 장점은 데이터가 서로 다른 호스트에 분산되어 노드당 스토리지를 절반만 사용한다는 점입니다. 더 중요한 점은 쿼리가 두 세그먼트에 걸쳐 처리되므로 메모리 활용 측면에서 더 효율적이고 호스트별 I/O도 줄어든다는 것입니다.

물론 이 클러스터 토폴로지의 가장 큰 단점은 호스트 중 하나를 잃으면 쿼리를 처리할 수 없게 된다는 점입니다.

다음 예시에서는 확장성과 장애 허용을 모두 제공하는 두 개의 세그먼트와 두 개의 레플리카로 구성된 클러스터를 설정하는 방법을 살펴보겠습니다.

Navigation