SLO, встроенные BACKUP/RESTORE, проверка RPO/RTO, безопасные обновления, масштабирование и разбор инцидентов
SLO, резервное копирование, безопасные обновления и восстановление после сбоев
Вы подготовите эксплуатационный контракт: измеримые SLO, модель отказов, процедуру обновления и проверенный сценарий BACKUP/RESTORE. Универсального набора настроек нет: значения зависят от нагрузки, бюджета и допустимого риска.
Пример отказоустойчивого кластера (6 серверов ClickHouse):
Шард 1 (2 реплики): Шард 2 (2 реплики): Шард 3 (2 реплики):
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ clickhouse-01-1 │ │ clickhouse-02-1 │ │ clickhouse-03-1 │
│ clickhouse-01-2 │ │ clickhouse-02-2 │ │ clickhouse-03-2 │
└─────────────────┘ └─────────────────┘ └─────────────────┘
Keeper/ZooKeeper (3 узла):
┌─────────┐ ┌─────────┐ ┌─────────┐
│ keeper1 │ │ keeper2 │ │ keeper3 │
└─────────┘ └─────────┘ └─────────┘
Это не минимальная конфигурация для любого проекта. Число шардов определяется объёмом и пропускной способностью, а число реплик — моделью отказа и требованиями доступности.
Принципы:
<!-- config.xml -->
<clickhouse>
<!-- Порты -->
<http_port>8123</http_port>
<tcp_port>9000</tcp_port>
<https_port>8443</https_port>
<tcp_port_secure>9440</tcp_port_secure>
<!-- Пути -->
<path>/var/lib/clickhouse/</path>
<tmp_path>/var/lib/clickhouse/tmp/</tmp_path>
<user_files_path>/var/lib/clickhouse/user_files/</user_files_path>
<format_schema_path>/var/lib/clickhouse/format_schemas/</format_schema_path>
<!-- Логирование -->
<logger>
<level>information</level>
<log>/var/log/clickhouse-server/clickhouse-server.log</log>
<errorlog>/var/lib/clickhouse-server/clickhouse-server.err.log</errorlog>
<size>100M</size>
<count>3</count>
</logger>
<!-- DNS кэширование -->
<dns_cache_disabled>true</dns_cache_disabled>
<!-- Максимальные настройки -->
<max_concurrent_queries>100</max_concurrent_queries>
<max_connections>4096</max_connections>
<!-- Keep-alive -->
<keep_alive_timeout>3</keep_alive_timeout>
<!-- Сжатие -->
<compression>
<case>
<min_part_size>10000000000</min_part_size>
<min_part_size_ratio>0.01</min_part_size_ratio>
<method>zstd</method>
</case>
</compression>
<!-- Грациозная остановка -->
<max_thread_pool_size>16000</max_thread_pool_size>
<shutdown_wait_unfinished>5</shutdown_wait_unfinished>
</clickhouse><!-- users.xml -->
<clickhouse>
<profiles>
<!-- Default профиль -->
<default>
<max_memory_usage>10000000000</max_memory_usage>
<max_execution_time>60</max_execution_time>
<max_rows_to_read>100000000</max_rows_to_read>
<max_bytes_to_read>100000000000</max_bytes_to_read>
<max_result_rows>1000000</max_result_rows>
<max_result_bytes>1000000000</max_result_bytes>
<readonly>0</readonly>
<!-- Оптимизации -->
<optimize_read_in_order>1</optimize_read_in_order>
<optimize_aggregation_in_order>1</optimize_aggregation_in_order>
<!-- JOIN -->
<join_algorithm>auto</join_algorithm>
<max_bytes_in_join>10000000000</max_bytes_in_join>
<!-- Агрегация -->
<max_bytes_before_external_group_by>20000000000</max_bytes_before_external_group_by>
<!-- Сортировка -->
<max_bytes_before_external_sort>20000000000</max_bytes_before_external_sort>
<!-- Сетевые настройки -->
<max_streams_to_max_threads_ratio>4</max_streams_to_max_threads_ratio>
</default>
<!-- Readonly профиль -->
<readonly>
<readonly>1</readonly>
<max_memory_usage>20000000000</max_memory_usage>
<max_execution_time>300</max_execution_time>
</readonly>
<!-- Heavy queries профиль -->
<heavy_queries>
<max_memory_usage>50000000000</max_memory_usage>
<max_execution_time>600</max_execution_time>
<max_bytes_before_external_group_by>50000000000</max_bytes_before_external_group_by>
</heavy_queries>
</profiles>
<quotas>
<default>
<interval>
<duration>3600</duration>
<queries>1000</queries>
<errors>100</errors>
<result_rows>10000000</result_rows>
<read_rows>1000000000</read_rows>
<execution_time>36000</execution_time>
</interval>
</default>
</quotas>
<users>
<default>
<password></password>
<networks>
<ip>::1</ip>
<ip>127.0.0.1</ip>
</networks>
<profile>default</profile>
<quota>default</quota>
<access_management>1</access_management>
</default>
<!-- Production приложение -->
<app_user>
<!-- Пароль передаётся через поддерживаемое секрет-хранилище,
не хранится в репозитории с конфигурацией. -->
<password_sha256_hex>REDACTED_HASH</password_sha256_hex>
<networks>
<ip>10.0.0.0/8</ip>
<ip>192.168.0.0/16</ip>
</networks>
<profile>default</profile>
<quota>default</quota>
<default_role>app_role</default_role>
</app_user>
</users>
</clickhouse>Репликация защищает от части аппаратных отказов, но не от ошибочного DROP, неверной мутации или повреждённых данных, которые разойдутся по репликам. Основной путь в актуальном ClickHouse — встроенные команды BACKUP и RESTORE.
Сначала администратор настраивает диск резервных копий или доступ к S3. Учётные данные лучше хранить в named collection: так они не оказываются в тексте запроса и system.query_log.
-- Полная копия базы в заранее настроенный диск
BACKUP DATABASE analytics
TO Disk('backups', 'analytics-full.zip')
SETTINGS id = 'analytics-full-20260810';
-- Асинхронная копия для долгой операции
BACKUP DATABASE analytics
TO Disk('backups', 'analytics-async.zip')
ASYNC
SETTINGS id = 'analytics-async-20260810';Статус операции проверяется по ID:
SELECT id, name, status, start_time, end_time, error
FROM system.backups
WHERE id IN ('analytics-full-20260810', 'analytics-async-20260810');BACKUP DATABASE analytics
TO Disk('backups', 'analytics-inc-20260811.zip')
SETTINGS
id = 'analytics-inc-20260811',
base_backup = Disk('backups', 'analytics-full.zip');Инкрементальная цепочка экономит место, но увеличивает число зависимостей при восстановлении. Политика хранения должна сохранять все необходимые базовые копии.
Не восстанавливайте учебную копию поверх рабочей базы. Используйте отдельный кластер или другое имя:
RESTORE DATABASE analytics AS analytics_restore_test
FROM Disk('backups', 'analytics-full.zip')
SETTINGS id = 'restore-drill-20260810';
SELECT
(SELECT count() FROM analytics.events) AS source_rows,
(SELECT count() FROM analytics_restore_test.events) AS restored_rows;Сравните не только число строк, но и контрольные агрегаты, DDL, словари и сущности доступа. Зафиксируйте фактические RPO и RTO. Бэкап, который ни разу не восстанавливали, остаётся непроверенной гипотезой.
RESTORE не охватывает всю базу одной транзакцией. При ошибке часть таблиц или частей уже может стать видимой, а автоматического отката нет. Поэтому восстановление выполняют в изоляции, проверяют статус каждой операции и только затем переключают приложение.
system.backups хранит операции только с последнего запуска сервера. Для долговременного аудита сохраняйте system.backup_log и внешние метрики.
FREEZE, снимки файловой системы и сторонние инструменты могут быть оправданы существующей инфраструктурой или специальными требованиями. Их нужно рассматривать как отдельный проект с тестом согласованности и восстановления, а не как автоматическую рекомендацию.
#!/bin/bash
# rolling_update.sh
CLUSTER_NODES=("ch-01" "ch-02" "ch-03" "ch-04" "ch-05" "ch-06")
for node in "${CLUSTER_NODES[@]}"; do
echo "Updating $node..."
# Исключить из балансировщика
disable_in_loadbalancer $node
# Остановить репликацию
ssh $node "clickhouse-client --query 'SYSTEM STOP FETCHES'"
# Дождаться завершения текущих запросов
ssh $node "clickhouse-client --query \"SELECT sleep(5)\""
# Остановить сервер
ssh $node "systemctl stop clickhouse-server"
# Обновить пакет
ssh $node "apt-get update && apt-get install -y clickhouse-server clickhouse-client"
# Запустить сервер
ssh $node "systemctl start clickhouse-server"
# Проверить статус
ssh $node "clickhouse-client --query 'SELECT 1'"
# Включить в балансировщик
enable_in_loadbalancer $node
# Дождаться синхронизации
ssh $node "clickhouse-client --query 'SYSTEM SYNC REPLICA'"
echo "Node $node updated successfully"
done-- Проверить версию перед обновлением
SELECT version();
-- Проверить совместимость версий
-- https://clickhouse.com/docs/en/whats-new/changelog/
-- Проверить статус реплик перед обновлением
SELECT
database,
table,
total_replicas,
active_replicas,
absolute_delay
FROM system.replicas;-- Добавить новый шард в кластер
-- 1. Обновить remote_servers.xml на всех узлах
-- 2. Создать таблицы на новых узлах
CREATE TABLE events_local ON CLUSTER cluster_default
(
event_time DateTime,
user_id UInt64
)
ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/events', '{replica}')
ORDER BY (event_time, user_id);
-- 3. Перераспределить данные (опционально)
INSERT INTO events_all
SELECT * FROM events_all
SETTINGS insert_distributed_sync = 1;<!-- Увеличить лимиты для мощного сервера -->
<max_concurrent_queries>200</max_concurrent_queries>
<max_memory_usage>100000000000</max_memory_usage> <!-- 100 GB -->
<max_threads>64</max_threads><!-- users.xml -->
<profiles>
<default>
<!-- Распределение запросов по репликам -->
<load_balancing>round_robin</load_balancing>
<!-- Предпочтение локальной реплике -->
<prefer_localhost_replica>1</prefer_localhost_replica>
</default>
</profiles>| Метрика | Порог | Описание |
|---|---|---|
OSFreePhysicalMemory | < 10% | Свободная память |
FilesystemFree | < 10% | Свободное место на диске |
ReplicasStatusAbsoluteDelay | > 60s | Отставание реплик |
MergesMutations | > 10 | Долгие слияния/мутации |
QueryLatency | P99 > 1s | Задержка запросов |
# clickhouse_alerts.yml
groups:
- name: clickhouse
rules:
- alert: ClickHouseLowDiskSpace
expr: clickhouse_filesystem_free / clickhouse_filesystem_total < 0.1
for: 5m
labels:
severity: critical
annotations:
summary: "Low disk space on {{ $labels.instance }}"
- alert: ClickHouseReplicaDelay
expr: clickhouse_replicas_status_absolute_delay > 60
for: 5m
labels:
severity: warning
annotations:
summary: "Replica delay on {{ $labels.instance }}"
- alert: ClickHouseHighMemory
expr: clickhouse_memory_tracking / clickhouse_max_memory_usage > 0.9
for: 5m
labels:
severity: warning
annotations:
summary: "High memory usage on {{ $labels.instance }}"-- Найти медленные запросы
SELECT
query,
avg(elapsed) AS avg_elapsed,
max(elapsed) AS max_elapsed,
count() AS executions
FROM system.query_log
WHERE event_date = today()
GROUP BY query
HAVING avg_elapsed > 1.0
ORDER BY avg_elapsed DESC
LIMIT 20;
-- Анализ плана
EXPLAIN PIPELINE
SELECT ...;-- Запросы с большим потреблением памяти
SELECT
query,
max(memory_usage) AS max_memory,
event_time
FROM system.query_log
WHERE event_date = today()
AND memory_usage > 10000000000 -- 10 GB
ORDER BY max_memory DESC;
-- Убить запрос
KILL QUERY WHERE query_id = '...';-- Проверить статус реплик
SELECT
database,
table,
is_readonly,
is_session_expired,
absolute_delay,
last_queue_update_exception
FROM system.replicas;
-- Проверить очередь репликации
SELECT
database,
table,
position,
type,
create_time,
latest_fail_reason
FROM system.replication_queue
WHERE latest_fail_reason != '';
-- Перезапустить репликацию
SYSTEM RESTART REPLICA table_name;Пример SLO для ClickHouse:
| Метрика | Цель | Период |
|---|---|---|
| Доступность | 99.9% | Месяц |
| Задержка запросов (P99) | < 1s | День |
| Отставание реплик | < 60s | Час |
| Потеря данных | 0 | Постоянно |
Расчёт доступности:
Доступность = (Время работы / Общее время) × 100%
99.9% = ~43 минуты простоя в месяц
BACKUP с явным ID и дождитесь статуса BACKUP_CREATED.Наличие архива без успешного RESTORE не засчитывается.
Переходите к итоговому практикуму: там схема, загрузка, оптимизация, безопасность и восстановление собираются в один проверяемый проект.
Далее: Итоговый практикум