Перейти к основному содержанию
Перейти к основному содержанию

Масштабирование

В этом примере вы узнаете, как настроить простой масштабируемый кластер ClickHouse. Настроено пять серверов: два используются для шардинга данных, а остальные три сервера выполняют функции координации.

Архитектура кластера, который вы будете настраивать, показана ниже:

Схема архитектуры для 2 шардов и 1 реплики
Примечание

Хотя можно запускать ClickHouse Server и ClickHouse Keeper вместе на одном сервере, мы настоятельно рекомендуем использовать выделенные хосты для ClickHouse Keeper в продукционных средах — именно этот подход мы продемонстрируем в данном примере.

Серверы Keeper могут быть менее мощными, и в большинстве случаев 4 ГБ ОЗУ достаточно для каждого сервера Keeper, пока ваши серверы ClickHouse существенно не вырастут.

Предварительные требования

Настройка структуры каталогов и тестовой среды

Пример файлов

Следующие шаги помогут вам настроить кластер с нуля. Если вы предпочитаете пропустить эти шаги и сразу перейти к запуску кластера, вы можете получить примерные файлы из репозитория ClickHouse Examples, каталог 'docker-compose-recipes'.

В этом руководстве вы будете использовать Docker Compose для развёртывания кластера ClickHouse. Эту конфигурацию можно адаптировать для работы на отдельных локальных машинах, виртуальных машинах или облачных инстансах.

Выполните следующие команды для создания структуры каталогов для этого примера:

mkdir cluster_2S_1R
cd cluster_2S_1R

# Создать директории clickhouse-keeper
for i in {01..03}; do
  mkdir -p fs/volumes/clickhouse-keeper-${i}/etc/clickhouse-keeper
done

# Создать директории clickhouse-server
for i in {01..02}; do
  mkdir -p fs/volumes/clickhouse-${i}/etc/clickhouse-server
done

Добавьте следующий файл docker-compose.yml в каталог clickhouse-cluster:

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 users.xml по умолчанию, который устанавливается вместе с каждым экземпляром ClickHouse.
Каталоги пользовательских конфигураций

Рекомендуется использовать каталоги config.d и users.d при написании собственной конфигурации, вместо того чтобы напрямую изменять конфигурацию по умолчанию в /etc/clickhouse-server/config.xml и etc/clickhouse-server/users.xml.

Строка

<clickhouse replace="true">

Обеспечивает, что разделы конфигурации, определённые в каталогах config.d и users.d, имеют приоритет над разделами конфигурации по умолчанию, заданными в файлах config.xml и users.xml.

Настройка узлов ClickHouse

Настройка сервера

Теперь измените каждый пустой файл конфигурации config.xml, расположенный по пути fs/volumes/clickhouse-{}/etc/clickhouse-server/config.d. Строки, выделенные ниже, должны быть изменены для каждого узла отдельно:

<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>
    <!--highlight-next-line-->
    <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>
    <!--highlight-start-->
    <macros>
        <shard>01</shard>
        <replica>01</replica>
    </macros>
    <!--highlight-end-->
</clickhouse>
КаталогФайл
fs/volumes/clickhouse-01/etc/clickhouse-server/config.dconfig.xml
fs/volumes/clickhouse-02/etc/clickhouse-server/config.dconfig.xml

Каждый раздел указанного выше конфигурационного файла подробно описан ниже.

Сеть и логирование

Внешние подключения к сетевому интерфейсу разрешаются путем активации параметра listen_host. Это гарантирует, что сервер ClickHouse доступен для других хостов:

<listen_host>0.0.0.0</listen_host>

Порт HTTP API установлен в значение 8123:

<http_port>8123</http_port>

TCP-порт для взаимодействия по собственному протоколу ClickHouse между clickhouse-client и другими нативными инструментами ClickHouse, а также между clickhouse-server и другими экземплярами clickhouse-server равен 9000:

<tcp_port>9000</tcp_port>

Логирование настраивается в блоке <logger>. Данная конфигурация создаёт отладочный журнал, который будет ротироваться при достижении размера 1000M три раза:

<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> и служит шаблоном для распределённых DDL-запросов — запросов, выполняемых на всём кластере с использованием конструкции ON CLUSTER. По умолчанию распределённые 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>

Для каждого сервера задаются следующие параметры:

ParameterDescriptionDefault Value
hostАдрес удалённого сервера. Можно использовать доменное имя либо IPv4- или IPv6-адрес. Если указан домен, при запуске сервер выполняет DNS-запрос и сохраняет результат на всё время своей работы. Если DNS-запрос завершился с ошибкой, сервер не запускается. При изменении DNS-записи сервер необходимо перезапустить.-
portTCP-порт для обмена сообщениями (tcp_port в конфигурации, обычно 9000). Не путать с http_port.-

Конфигурация Keeper

Секция <ZooKeeper> указывает ClickHouse, где запущен ClickHouse Keeper (или ZooKeeper). Поскольку используется кластер 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>
Примечание

Хотя ClickHouse Keeper можно запустить на том же сервере, что и ClickHouse Server, для production-окружений мы настоятельно рекомендуем использовать выделенные хосты для ClickHouse Keeper.

Конфигурация макросов

Кроме того, секция <macros> используется для определения подстановки параметров для реплицируемых таблиц. Они перечислены в system.macros и позволяют использовать подстановки типа {shard} и {replica} в запросах.

<macros>
    <shard>01</shard>
    <replica>01</replica>
</macros>
Примечание

Эти параметры определяются индивидуально в зависимости от конфигурации кластера.

Настройка пользователя

Теперь измените каждый пустой конфигурационный файл users.xml, расположенный в fs/volumes/clickhouse-{}/etc/clickhouse-server/users.d, следующим образом:

<?xml version="1.0"?>
<clickhouse replace="true">
    <profiles>
        <default>
            <max_memory_usage>10000000000</max_memory_usage>
            <use_uncompressed_cache>0</use_uncompressed_cache>
            <load_balancing>in_order</load_balancing>
            <log_queries>1</log_queries>
        </default>
    </profiles>
    <users>
        <default>
            <access_management>1</access_management>
            <profile>default</profile>
            <networks>
                <ip>::/0</ip>
            </networks>
            <quota>default</quota>
            <access_management>1</access_management>
            <named_collection_control>1</named_collection_control>
            <show_named_collections>1</show_named_collections>
            <show_named_collections_secrets>1</show_named_collections_secrets>
        </default>
    </users>
    <quotas>
        <default>
            <interval>
                <duration>3600</duration>
                <queries>0</queries>
                <errors>0</errors>
                <result_rows>0</result_rows>
                <read_rows>0</read_rows>
                <execution_time>0</execution_time>
            </interval>
        </default>
    </quotas>
</clickhouse>
СодержаниеФайл
fs/volumes/clickhouse-01/etc/clickhouse-server/users.dusers.xml
fs/volumes/clickhouse-02/etc/clickhouse-server/users.dusers.xml

В данном примере пользователь по умолчанию настроен без пароля для упрощения. На практике это не рекомендуется.

Примечание

В данном примере файл users.xml одинаков для всех узлов кластера.

Настройка ClickHouse Keeper

Настройка Keeper

Чтобы репликация работала, необходимо развернуть и настроить кластер ClickHouse Keeper. ClickHouse Keeper предоставляет систему координации для репликации данных, выступая полноценной заменой Zookeeper, который также может использоваться. Однако рекомендуется использовать ClickHouse Keeper, так как он обеспечивает более надежные гарантии, выше общую надежность и использует меньше ресурсов, чем ZooKeeper. Для высокой доступности и сохранения кворума рекомендуется запускать как минимум три узла ClickHouse Keeper.

Примечание

ClickHouse Keeper может работать на любом узле кластера вместе с ClickHouse, хотя рекомендуется запускать его на выделенном узле, что позволяет масштабировать и управлять кластером ClickHouse Keeper независимо от кластера базы данных.

Создайте файлы keeper_config.xml для каждого узла ClickHouse Keeper, используя следующую команду из корневого каталога примера:

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 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>
        <!--highlight-next-line-->
        <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-keeperkeeper_config.xml
fs/volumes/clickhouse-keeper-02/etc/clickhouse-keeperkeeper_config.xml
fs/volumes/clickhouse-keeper-03/etc/clickhouse-keeperkeeper_config.xml

Каждый файл конфигурации будет содержать следующую уникальную конфигурацию (см. ниже). server_id должен быть уникальным для данного узла ClickHouse Keeper в кластере и соответствовать серверу <id>, определённому в разделе <raft_configuration>. 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>
        <!-- TCP-порт для взаимодействия между узлами ClickHouse Keeper -->
        <!--highlight-next-line-->
        <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>
ClickHouse Cloud упрощает управление

ClickHouse Cloud снимает операционную нагрузку, связанную с управлением шардами и репликами. Платформа автоматически обеспечивает высокую доступность, репликацию и принятие решений о масштабировании. Вычислительные ресурсы и хранилище разделены и автоматически масштабируются по мере необходимости, не требуя ручной настройки и постоянного обслуживания.

Подробнее

Проверка настройки

Убедитесь, что Docker запущен на вашем компьютере. Запустите кластер командой docker-compose up из корневого каталога cluster_2S_1R:

docker-compose up -d

Вы увидите, как docker начнет загружать образы ClickHouse и Keeper, а затем запустит контейнеры:

[+] Running 6/6
 ✔ Network cluster_2s_1r_default   Создано
 ✔ Container clickhouse-keeper-03  Запущено
 ✔ Container clickhouse-keeper-02  Запущено
 ✔ Container clickhouse-keeper-01  Запущено
 ✔ Container clickhouse-01         Запущено
 ✔ Container clickhouse-02         Запущено

Чтобы проверить, что кластер работает, подключитесь к clickhouse-01 или clickhouse-02 и выполните следующий запрос. Ниже приведена команда для подключения к первому узлу:

# Подключитесь к любому узлу
docker exec -it clickhouse-01 clickhouse-client

При успешном подключении вы увидите командную строку клиента ClickHouse:

cluster_2S_1R node 1 :)

Выполните следующий запрос, чтобы проверить, какие топологии кластера определены для каких хостов:

SELECT 
    cluster,
    shard_num,
    replica_num,
    host_name,
    port
FROM system.clusters;
┌─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:

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

Команда mntr также часто используется для проверки того, что ClickHouse Keeper запущен, и для получения информации о состоянии и взаимоотношениях трёх узлов Keeper. В конфигурации, используемой в этом примере, три узла работают совместно. Узлы выбирают лидера, а остальные узлы становятся ведомыми.

Команда mntr предоставляет информацию, связанную с производительностью, а также о том, является ли конкретный узел лидером или ведомым.

Совет

Вам может потребоваться установить netcat, чтобы отправить команду mntr Keeper’у. См. страницу nmap.org для получения информации о скачивании.

Выполните приведённую ниже команду в оболочке на clickhouse-keeper-01, clickhouse-keeper-02 и clickhouse-keeper-03, чтобы проверить состояние каждого узла Keeper. Команда для clickhouse-keeper-01 показана ниже:

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

Ниже приведён пример ответа от ведомого узла:

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
# highlight-next-line
zk_server_state ведомый
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

Ниже приведён пример ответа от ведущего узла:

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
# highlight-next-line
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
# highlight-start
zk_followers    2
zk_synced_followers     2
# highlight-end

Таким образом, вы успешно настроили кластер ClickHouse с одним шардом и двумя репликами. На следующем шаге вы создадите таблицу в кластере.

Создание базы данных

Теперь, когда вы убедились, что кластер правильно настроен и запущен, вы создадите ту же таблицу, что и в руководстве по примеру набора данных UK property prices. Она содержит около 30 миллионов записей о ценах на недвижимость в Англии и Уэльсе с 1995 года.

Подключитесь к клиенту каждого хоста, выполнив следующие команды в отдельных вкладках или окнах терминала:

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

Вы можете выполнить приведённый ниже запрос из clickhouse-client на каждом хосте, чтобы убедиться, что помимо стандартных баз данных других пока не создано:

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

Из клиента clickhouse-01 выполните следующий распределённый DDL-запрос с использованием конструкции ON CLUSTER для создания новой базы данных uk:

CREATE DATABASE IF NOT EXISTS uk 
-- highlight-next-line
ON CLUSTER cluster_2S_1R;

Вы можете снова выполнить тот же запрос, что и ранее, из клиента каждого хоста, чтобы убедиться, что база данных была создана во всём кластере, несмотря на то, что запрос выполнялся только на clickhouse-01:

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

Создание таблицы в кластере

Теперь, когда база данных создана, создайте таблицу. Выполните следующий запрос из любого клиента на хосте:

CREATE TABLE IF NOT EXISTS uk.uk_price_paid_local
--highlight-next-line
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 в руководстве по примеру набора данных цен на недвижимость в Великобритании, за исключением конструкции ON CLUSTER.

Конструкция ON CLUSTER предназначена для распределённого выполнения DDL-запросов (Data Definition Language), таких как CREATE, DROP, ALTER и RENAME, что обеспечивает применение изменений схемы на всех узлах кластера.

Вы можете выполнить приведённый ниже запрос из клиента каждого хоста, чтобы убедиться, что таблица создана во всём кластере:

SHOW TABLES IN uk;
┌─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:

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

Теперь из clickhouse-01 или clickhouse-02 выполните следующий запрос:

-- из clickhouse-01
SELECT * FROM test.test_table;
--   ┌─id─┬─name───────────────┐
-- 1.│  1 │ Clicky McClickface │
--   └────┴────────────────────┘

-- из 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;

После вставки данных можно проверить количество строк с помощью распределённой таблицы:

SELECT count(*)
FROM uk.uk_price_paid_distributed
┌──count()─┐
1. │ 30212555 │ -- 30,21 миллиона
   └──────────┘

Если выполнить следующий запрос на любом из хостов, вы увидите, что данные распределены более или менее равномерно по шардам (имейте в виду, что выбор шарда для вставки определялся функцией rand(), поэтому ваши результаты могут отличаться):

-- from clickhouse-01
SELECT count(*)
FROM uk.uk_price_paid_local
--    ┌──count()─┐
-- 1. │ 15107353 │ -- 15,11 миллионов
--    └──────────┘

--from clickhouse-02
SELECT count(*)
FROM uk.uk_price_paid_local
--    ┌──count()─┐
-- 1. │ 15105202 │ -- 15,11 миллионов
--    └──────────┘

Что произойдет, если один из хостов выйдет из строя? Давайте смоделируем это, остановив clickhouse-01:

docker stop clickhouse-01

Проверьте, что хост недоступен, выполнив команду:

docker-compose ps
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
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))
#highlight-next-line
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)

К сожалению, наш кластер не обладает отказоустойчивостью. При отказе одного из хостов кластер считается неработоспособным, и запрос завершается с ошибкой — в отличие от реплицированной таблицы из предыдущего примера, где мы могли вставлять данные даже при отказе одного из хостов.

Заключение

Преимущество такой топологии кластера состоит в том, что данные распределяются по отдельным хостам и используют вдвое меньше дискового пространства на узел. Что ещё важнее, запросы обрабатываются на обоих шардах, что более эффективно с точки зрения использования памяти и снижает объём I/O на каждый хост.

Основной недостаток такой топологии кластера, разумеется, в том, что потеря одного из хостов делает обработку запросов невозможной.

В следующем примере мы рассмотрим, как настроить кластер с двумя шардами и двумя репликами, обеспечивающий как масштабируемость, так и отказоустойчивость.