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

Предварительные требования
- Вы уже настраивали локальный сервер 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:
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содержит файл конфигурации сервера ClickHouseconfig.xml, в котором задаётся пользовательская конфигурация для каждого узла ClickHouse. Эта конфигурация объединяется с файлом конфигурации ClickHouseconfig.xmlпо умолчанию, который входит в состав каждой установки ClickHouse. - Каталог
users.dсодержит файл пользовательской конфигурацииusers.xml, в котором задаётся пользовательская конфигурация для пользователей. Эта конфигурация объединяется с файлом конфигурации ClickHouseusers.xmlпо умолчанию, который входит в состав каждой установки ClickHouse.
Настройка узлов 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>
<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>TCP-порт для взаимодействия по собственному протоколу ClickHouse между clickhouse-client
и другими инструментами ClickHouse, а также между clickhouse-server и другими clickhouse-servers,
установлен в 9000:
<tcp_port>9000</tcp_port>Логирование настраивается в блоке <logger>. Приведённая ниже конфигурация
задаёт отладочный лог с ротацией по достижении 1000 МБ, не более трёх файлов:
<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>Для каждого сервера указываются следующие параметры:
| Параметр | Описание | Значение по умолчанию |
|---|---|---|
host |
Адрес удалённого сервера. Можно использовать либо доменное имя, либо IPv4- или IPv6-адрес. Если указать доменное имя, при запуске сервер выполняет DNS-запрос, и результат сохраняется на всё время его работы. Если DNS-запрос завершается ошибкой, сервер не запустится. Если изменить DNS-запись, сервер нужно перезапустить. | - |
port |
TCP-порт для обмена сообщениями (tcp_port в конфигурации, обычно 9000). Не путать с http_port. |
- |
Конфигурация Keeper
Раздел <ZooKeeper> указывает ClickHouse, где запущен ClickHouse Keeper (или ZooKeeper).
Поскольку мы используем кластер Keeper, необходимо указать каждый <node> кластера
вместе с его hostname и номером порта с помощью тегов <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>Конфигурация пользователя
Теперь внесите следующие изменения в каждый пустой конфигурационный файл 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>| Каталог | File |
|---|---|
fs/volumes/clickhouse-01/etc/clickhouse-server/users.d |
users.xml |
fs/volumes/clickhouse-02/etc/clickhouse-server/users.d |
users.xml |
В данном примере default user настроен без пароля для удобства. На практике такой подход не рекомендуется.
Настройка ClickHouse Keeper
Настройка Keeper
Чтобы репликация работала, необходимо развернуть и настроить кластер ClickHouse Keeper. ClickHouse Keeper обеспечивает систему координации для репликации данных, выступая в качестве замены ZooKeeper, который также можно использовать. Однако рекомендуется использовать ClickHouse Keeper, так как он обеспечивает более строгие гарантии и надежность, а также потребляет меньше ресурсов, чем ZooKeeper. Для Высокой доступности и сохранения кворума рекомендуется запускать как минимум три узла 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>
<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 должен быть уникальным для соответствующего узла 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 -->
<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.
Запустите кластер командой docker-compose up из корневого каталога cluster_2S_1R:
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:
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.
В конфигурации, используемой в этом примере, вместе работают три узла.
Они выберут leader, а остальные узлы будут followers.
Команда mntr предоставляет информацию о производительности, а также о том,
является ли конкретный узел follower или leader.
Выполните приведённую ниже команду в оболочке на 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
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Ниже приведён пример ответа от узла-лидера:
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 с двумя сегментами, в каждом из которых по одной реплике. На следующем шаге вы создадите таблицу в кластере.
Создайте базу данных
Теперь, когда вы убедились, что кластер правильно настроен и работает, вы воссоздадите ту же таблицу, которая используется в руководстве по работе с 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
ON CLUSTER cluster_2S_1R;Вы можете снова выполнить тот же запрос, что и раньше, из клиента на каждом хосте,
чтобы убедиться, что база данных создана во всём кластере, хотя запрос был выполнен
только на clickhouse-01:
SHOW DATABASES; ┌─name───────────────┐
1. │ INFORMATION_SCHEMA │
2. │ default │
3. │ information_schema │
4. │ system │
5. │ uk │
└────────────────────┘Создайте таблицу в кластере
Теперь, когда база данных создана, создайте таблицу. Выполните следующий запрос с любого из хост-клиентов:
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 в руководстве по демонстрационному набору данных
UK property prices, за исключением предложения ON CLUSTER.
Предложение ON CLUSTER предназначено для распределённого выполнения DDL-запросов (Data Definition Language),
таких как CREATE, DROP, ALTER и RENAME, и гарантирует, что эти
изменения схемы будут применены на всех узлах кластера.
Вы можете выполнить приведённый ниже запрос из клиента на каждом хосте, чтобы убедиться, что таблица была создана во всём кластере:
SHOW TABLES IN uk; ┌─name────────────────┐
1. │ uk_price_paid_local │
└─────────────────────┘Прежде чем выполнять вставку данных UK price paid, давайте проведем небольшой эксперимент и посмотрим, что произойдет, если вставить данные в обычную таблицу с любого из хостов.
Создайте тестовую базу данных и таблицу, выполнив следующий запрос с любого из хостов:
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 выполните следующий запрос:
-- 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 таблица и создаётся с помощью
движка таблицы Distributed. Давайте посмотрим, как это работает.
Создание distributed таблицы
Создайте 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(), поэтому
вставка случайным образом распределяется по сегментам.
Теперь выполните запрос к distributed таблице с любого из хостов, и вы получите обе строки, вставленные на двух хостах, в отличие от предыдущего примера:
SELECT * FROM test.test_table_dist; ┌─id─┬─name───────────────┐
1. │ 1 │ Alexey Milovidov │
2. │ 1 │ Clicky McClickface │
└────┴────────────────────┘Давайте сделаем то же самое для наших данных о ценах на недвижимость в Великобритании. С любого из клиентских хостов
выполните следующий запрос, чтобы создать distributed таблицу, используя существующую таблицу,
которую мы ранее создали с помощью 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());Вставка данных в distributed таблицу
Теперь подключитесь к любому из хостов и вставьте данные:
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 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 psNAME 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_distributedReceived 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)К сожалению, наш кластер не является отказоустойчивым. Если один из хостов выходит из строя, кластер считается неработоспособным и запрос завершается с ошибкой — в отличие от реплицированной таблицы из предыдущего примера, в которую мы могли вставлять данные даже при отказе одного из хостов.
Заключение
Преимущество этой топологии кластера в том, что данные распределяются по отдельным хостам и требуют вдвое меньше дискового пространства на каждом узле. Что еще важнее, запросы обрабатываются на обоих сегментах, что эффективнее с точки зрения использования памяти и снижает I/O на каждом хосте.
Основной недостаток этой топологии кластера, разумеется, в том, что потеря одного из хостов лишает нас возможности выполнять запросы.
В следующем примере мы рассмотрим, как настроить кластер с двумя сегментами и двумя репликами, обеспечивающий как масштабируемость, так и отказоустойчивость.