Skip to content
ClickHouse Docs
ClickHouse DocsClickHouse Docs

Escalonamento

Neste exemplo, você aprenderá a configurar um cluster ClickHouse simples com escalabilidade. Há cinco servidores configurados. Dois são usados para fazer sharding dos dados. Os outros três são usados para coordenação.

A arquitetura do cluster que você vai configurar é mostrada abaixo:

Diagrama de arquitetura para 2 shards e 1 réplica

Pré-requisitos

Configurar a estrutura de diretórios e o ambiente de teste

Neste tutorial, você usará o Docker compose para configurar o cluster do ClickHouse. Esta configuração pode ser adaptada para funcionar em máquinas locais separadas, máquinas virtuais ou instâncias de nuvem também.

Execute os seguintes comandos para configurar a estrutura de diretórios deste exemplo:

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

Adicione o seguinte arquivo docker-compose.yml ao diretório 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

Crie os seguintes subdiretórios e arquivos:

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
  • O diretório config.d contém o arquivo de configuração do servidor ClickHouse config.xml, no qual é definida a configuração personalizada de cada nó do ClickHouse. Essa configuração é combinada com o arquivo de configuração padrão config.xml do ClickHouse, incluído em toda instalação do ClickHouse.
  • O diretório users.d contém o arquivo de configuração de usuários users.xml, no qual é definida a configuração personalizada dos usuários. Essa configuração é combinada com o arquivo de configuração padrão users.xml do ClickHouse, incluído em toda instalação do ClickHouse.

Configurar nós do ClickHouse

Configuração do servidor

Agora modifique cada arquivo de configuração vazio config.xml localizado em fs/volumes/clickhouse-{}/etc/clickhouse-server/config.d. As linhas que estão destacadas abaixo precisam ser alteradas para serem específicas de cada nó:

<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>
Diretório File
fs/volumes/clickhouse-01/etc/clickhouse-server/config.d config.xml
fs/volumes/clickhouse-02/etc/clickhouse-server/config.d config.xml

Cada seção do arquivo de configuração acima é explicada em mais detalhes a seguir.

Rede e logging

A comunicação externa pela interface de rede é habilitada ao ativar a configuração listen_host. Isso garante que o host do servidor ClickHouse possa ser acessado por outros hosts:

<listen_host>0.0.0.0</listen_host>

A porta da API HTTP está configurada como 8123:

<http_port>8123</http_port>

A porta TCP usada para comunicação pelo protocolo nativo do ClickHouse entre clickhouse-client e outras ferramentas nativas do ClickHouse, e entre clickhouse-server e outros clickhouse-servers é definida como 9000:

<tcp_port>9000</tcp_port>

O logging é definido no bloco <logger>. Esta configuração de exemplo fornece um log de depuração que será rotacionado em 1000M três vezes:

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

Para mais informações sobre a configuração de logging, consulte os comentários incluídos no arquivo de configuração padrão do ClickHouse.

Configuração do cluster

A configuração do cluster é definida no bloco <remote_servers>. Aqui, o nome do cluster cluster_2S_1R é definido.

O bloco <cluster_2S_1R></cluster_2S_1R> define o layout do cluster, utilizando as configurações <shard></shard> e <replica></replica>, e atua como um template para consultas de DDL distribuído, que são queries executadas em todo o cluster por meio da cláusula ON CLUSTER. Por padrão, as queries de DDL distribuído são permitidas, mas também podem ser desativadas com a configuração allow_distributed_ddl_queries.

internal_replication é mantido como false por padrão, pois há apenas uma réplica por shard.

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

Para cada servidor, os seguintes parâmetros são especificados:

Parâmetro Descrição Valor padrão
host O endereço do servidor remoto. Você pode usar o nome de domínio ou o endereço IPv4 ou IPv6. Se especificar o domínio, o servidor faz uma consulta DNS ao iniciar, e o resultado é mantido enquanto o servidor estiver em execução. Se a consulta DNS falhar, o servidor não será iniciado. Se você alterar o registro DNS, precisará reiniciar o servidor. -
port A porta TCP para a comunicação do messenger (tcp_port na configuração, normalmente definida como 9000). Não confundir com http_port. -

Configuração do Keeper

A seção <ZooKeeper> informa ao ClickHouse onde o ClickHouse Keeper (ou ZooKeeper) está em execução. Como estamos usando um cluster do ClickHouse Keeper, cada <node> do cluster precisa ser especificado, junto com seu hostname e número de porta, por meio das tags <host> e <port>, respectivamente.

A configuração do ClickHouse Keeper é explicada na próxima etapa do tutorial.

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

Configuração de macros

Além disso, a seção <macros> é usada para definir substituições de parâmetros para tabelas replicadas. Elas são listadas em system.macros e permitem o uso de substituições como {shard} e {replica} em queries.

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

Configuração de usuário

Agora modifique cada arquivo de configuração vazio users.xml localizado em fs/volumes/clickhouse-{}/etc/clickhouse-server/users.d com o seguinte conteúdo:

/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>
Diretório File
fs/volumes/clickhouse-01/etc/clickhouse-server/users.d users.xml
fs/volumes/clickhouse-02/etc/clickhouse-server/users.d users.xml

Neste exemplo, o usuário padrão é configurado sem senha para simplificar. Na prática, isso não é recomendado.

Configurar o ClickHouse Keeper

Configuração do ClickHouse Keeper

Para que a replicação funcione, é necessário configurar e implantar um cluster do ClickHouse Keeper. O ClickHouse Keeper fornece o sistema de coordenação para a replicação de dados, atuando como um substituto direto do ZooKeeper, que também pode ser usado. No entanto, o ClickHouse Keeper é recomendado, pois oferece melhores garantias e confiabilidade, além de usar menos recursos do que o ZooKeeper. Para alta disponibilidade e para manter o quórum, recomenda-se executar pelo menos três nós do ClickHouse Keeper.

Crie os arquivos keeper_config.xml para cada nó do ClickHouse Keeper usando o comando abaixo a partir da raiz da pasta de exemplo:

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

Modifique os arquivos de configuração vazios criados em cada diretório de nó fs/volumes/clickhouse-keeper-{}/etc/clickhouse-keeper. As linhas destacadas abaixo precisam ser ajustadas para cada nó:

/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>
Diretório Arquivo
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

Cada arquivo de configuração conterá a seguinte configuração exclusiva (mostrada abaixo). O server_id usado deve ser exclusivo para esse nó específico do ClickHouse Keeper no cluster e corresponder ao <id> do servidor definido na seção <raft_configuration>. tcp_port é a porta usada pelos clientes do ClickHouse Keeper.

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

A seção a seguir é usada para configurar os servidores que participam do quórum do algoritmo de consenso Raft:

<raft_configuration>
    <server>
        <id>1</id>
        <hostname>clickhouse-keeper-01</hostname>
        <!-- Porta TCP usada para comunicação entre os nós do ClickHouse Keeper -->
        <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>

Teste a configuração

Certifique-se de que o Docker esteja em execução na sua máquina. Inicie o cluster usando o comando docker-compose up na raiz do diretório cluster_2S_1R:

docker-compose up -d

Você deverá ver o Docker começar a baixar as imagens do ClickHouse e do Keeper e, em seguida, iniciar os contêineres:

[+] 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

Para verificar se o cluster está em execução, conecte-se a clickhouse-01 ou clickhouse-02 e execute a consulta a seguir. O comando para se conectar ao primeiro nó é mostrado abaixo:

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

Se tudo der certo, você verá o prompt do cliente ClickHouse:

cluster_2S_1R node 1 :)

Execute a seguinte consulta para verificar quais topologias de cluster estão definidas para quais hosts:

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 │
   └───────────────┴───────────┴─────────────┴───────────────┴──────┘

Execute a consulta a seguir para verificar o status do cluster do 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     │       │ /           │
   └────────────┴───────┴─────────────┘

O comando mntr também é comumente usado para verificar se o ClickHouse Keeper está em execução e para obter informações de estado sobre a relação entre os três nós do Keeper. Na configuração usada neste exemplo, há três nós trabalhando juntos. Os nós elegerão um líder, e os nós restantes serão seguidores.

O comando mntr fornece informações relacionadas ao desempenho e indica se um determinado nó é seguidor ou líder.

Execute o comando abaixo em um shell no clickhouse-keeper-01, clickhouse-keeper-02 e clickhouse-keeper-03 para verificar o status de cada nó do Keeper. O comando para clickhouse-keeper-01 é mostrado abaixo:

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

A resposta abaixo mostra um exemplo de resposta de um nó seguidor:

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

A resposta abaixo mostra um exemplo de resposta de um nó líder:

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

Com isso, você concluiu com sucesso a configuração de um cluster ClickHouse com dois shards e uma réplica por shard. Na próxima etapa, você criará uma tabela no cluster.

Criar um banco de dados

Agora que você verificou que o cluster está configurado corretamente e em execução, você vai recriar a mesma tabela usada no tutorial do conjunto de dados de exemplo UK property prices. Ela contém cerca de 30 milhões de linhas com preços pagos por imóveis na Inglaterra e no País de Gales desde 1995.

Conecte-se ao cliente de cada host executando cada um dos comandos a seguir em abas ou janelas separadas do terminal:

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

Você pode executar a consulta abaixo no clickhouse-client de cada host para confirmar que ainda não há bancos de dados criados, além dos padrão:

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

No cliente clickhouse-01, execute a seguinte consulta DDL distribuída usando a cláusula ON CLUSTER para criar um novo banco de dados chamado uk:

CREATE DATABASE IF NOT EXISTS uk 
ON CLUSTER cluster_2S_1R;

Você pode executar novamente a mesma consulta de antes a partir do cliente de cada host para confirmar que o banco de dados foi criado em todo o cluster, apesar de a consulta ter sido executada apenas em clickhouse-01:

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

Criar uma tabela no cluster

Agora que o banco de dados foi criado, você vai criar uma tabela. Execute a consulta a seguir em qualquer um dos hosts cliente:

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

Observe que ela é idêntica à consulta usada na instrução CREATE original do tutorial do conjunto de dados de exemplo UK property prices, exceto pela cláusula ON CLUSTER.

A cláusula ON CLUSTER foi projetada para a execução distribuída de consultas DDL (Data Definition Language), como CREATE, DROP, ALTER e RENAME, garantindo que essas alterações de esquema sejam aplicadas em todos os nós de um cluster.

Você pode executar a consulta abaixo no cliente de cada host para confirmar que a tabela foi criada em todo o cluster:

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

Antes de inserirmos os dados de preços pagos do Reino Unido, vamos fazer um experimento rápido para ver o que acontece quando inserimos dados em uma tabela comum a partir de qualquer host.

Crie um banco de dados e uma tabela de teste com a consulta a seguir a partir de qualquer host:

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;

Agora, no clickhouse-01, execute a seguinte consulta INSERT:

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

Alterne para clickhouse-02 e execute a seguinte consulta INSERT:

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

Agora, em clickhouse-01 ou clickhouse-02, execute a seguinte consulta:

-- 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   │
--   └────┴────────────────────┘

Você vai notar que, diferentemente de uma tabela ReplicatedMergeTree, apenas a linha que foi inserida na tabela naquele host específico é retornada, e não as duas linhas.

Para ler os dados nos dois shards, precisamos de uma interface capaz de processar consultas em todos os shards, combinando os dados de ambos os shards quando executamos consultas SELECT nela ou inserindo dados em ambos os shards quando executamos consultas INSERT.

No ClickHouse, essa interface é chamada de tabela distribuída, e é criada usando o motor de tabela Distributed. Vamos ver como ela funciona.

Criar uma tabela distribuída

Crie uma tabela distribuída com a seguinte consulta:

CREATE TABLE test.test_table_dist ON CLUSTER cluster_2S_1R AS test.test_table
ENGINE = Distributed('cluster_2S_1R', 'test', 'test_table', rand())

Neste exemplo, a função rand() é escolhida como a chave de sharding para que as inserções sejam distribuídas aleatoriamente pelos shards.

Agora consulte a tabela distribuída em qualquer um dos hosts, e você verá as duas linhas que foram inseridas nos dois hosts, ao contrário do exemplo anterior:

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

Vamos fazer o mesmo com nossos dados de preços de imóveis no Reino Unido. Em qualquer um dos hosts clientes, execute a seguinte consulta para criar uma tabela distribuída usando a tabela existente que criamos anteriormente com 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());

Inserir dados em uma tabela distribuída

Agora conecte-se a um dos hosts e insira os dados:

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;

Após a inserção dos dados, você pode verificar o número de linhas usando a tabela distribuída:

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

Se você executar a consulta a seguir em qualquer um dos hosts, verá que os dados foram distribuídos de forma mais ou menos uniforme entre os shards (lembrando que a escolha de qual shard receberia o insert foi definida com rand(), portanto os resultados podem variar):

-- 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
--    └──────────┘

O que acontece se um dos hosts falhar? Vamos simular isso desligando clickhouse-01:

docker stop clickhouse-01

Verifique se o host está inativo executando:

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

Agora, a partir do clickhouse-02, execute a mesma consulta select que executamos antes na tabela distribuída:

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)

Infelizmente, nosso cluster não é tolerante a falhas. Se um dos hosts falhar, o cluster é considerado indisponível e a consulta falha — diferentemente da tabela replicada que vimos no exemplo anterior, na qual conseguíamos inserir dados mesmo quando um dos hosts falhava.

Conclusão

A vantagem desta topologia de cluster é que os dados ficam distribuídos entre hosts separados e usam metade do armazenamento por nó. Mais importante ainda, as consultas são processadas em ambos os shards, o que é mais eficiente em termos de uso de memória e reduz a E/S por host.

A principal desvantagem desta topologia de cluster é, claro, que a perda de um dos hosts nos impede de atender consultas.

No próximo exemplo, veremos como configurar um cluster com dois shards e duas réplicas, oferecendo escalabilidade e tolerância a falhas.

Navigation