> ## Documentation Index
> Fetch the complete documentation index at: https://clickhouse.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

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

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

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

В этом руководстве описано, как масштабировать реплики и сегменты `ClickHouseCluster`, как безопасно масштабировать кворум `KeeperCluster` и за какими состояниями нужно следить во время операции масштабирования.

<Note>
  Для `ClickHouseCluster` всегда требуется Keeper, на который ссылается обязательное поле `spec.keeperClusterRef` — оператор координирует кластер через него независимо от размера. Чтобы запускать более одной реплики на сегмент, данные также должны храниться в таблицах `ReplicatedMergeTree`, поскольку именно репликация позволяет второй реплике обслуживать те же строки.
</Note>

<div id="scaling-replicas">
  ## Масштабирование реплик
</div>

`spec.replicas` задаёт количество реплик в каждом сегменте. Каждая реплика запускается в собственном StatefulSet с именем `<cluster>-clickhouse-<shard>-<replica>`, поэтому в кластере с `shards: 2` и `replicas: 3` будет запущено шесть StatefulSets.

Увеличьте или уменьшите это количество прямо в существующей конфигурации:

```yaml theme={null}
spec:
  replicas: 3   # was 1
  keeperClusterRef:
    name: my-keeper
```

При масштабировании вверх оператор создает новые StatefulSets для каждой реплики, ждет, пока каждый под перейдет в состояние Ready, а затем синхронизирует схему с новыми репликами (см. [Автоматическая синхронизация схемы](#automatic-schema-sync)). При масштабировании вниз он удаляет лишние StatefulSets и очищает устаревшие записи о репликах реплицируемой базы данных, оставшиеся после удаления реплик.

<div id="scaling-shards">
  ## Масштабирование сегментов
</div>

`spec.shards` задаёт количество сегментов. Каждый новый сегмент добавляет полный набор StatefulSet для каждой реплики, а оператор создаёт один [PodDisruptionBudget на сегмент](/docs/ru/products/kubernetes-operator/guides/configuration#pod-disruption-budgets), чтобы сбой в одном сегменте не учитывался при расчёте для другого.

```yaml theme={null}
spec:
  shards: 3   # was 1
  replicas: 2
```

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

<div id="automatic-schema-sync">
  ## Автоматическая синхронизация схемы
</div>

Когда `spec.settings.enableDatabaseSync` имеет значение `true` (по умолчанию), оператор поддерживает согласованность схемы при изменении топологии:

* **При масштабировании вверх** — как только готовы как минимум две реплики, оператор реплицирует определения баз данных на вновь созданные реплики, чтобы новая реплика присоединилась с теми же базами данных `Replicated` и базами данных интеграций, что и остальные узлы кластера.
* **При масштабировании вниз** — прежде чем реплика исчезнет, оператор удаляет регистрацию этой реплики из каждой базы данных `Replicated` с помощью `SYSTEM DROP DATABASE REPLICA`, чтобы уменьшенный кластер не ожидал реплику базы данных `Replicated`, которой больше не существует.

Это относится к базам данных `Replicated` и движкам баз данных интеграций. Табличные данные при этом не перемещаются — данные строк хранятся в таблицах `ReplicatedMergeTree` и реплицируются через Keeper независимо от этой синхронизации схемы. Если готова только одна реплика, реплицировать некуда, поэтому оператор пропускает этот шаг и записывает в журнал, что целевой реплики нет.

Установите `enableDatabaseSync: false`, чтобы отключить это поведение, например если за распространение схемы отвечает внешний инструмент. В этом случае оператор указывает причину `SchemaSyncDisabled` в условии `SchemaInSync`.

<div id="scaling-conditions">
  ## Состояния, за которыми стоит следить
</div>

Проверяйте состояние пользовательского ресурса во время операции масштабирования:

```bash theme={null}
kubectl get clickhousecluster sample -o yaml | sed -n '/conditions:/,/^[^ ]/p'
```

| Условие              | Причина                | Значение                                                                  |
| -------------------- | ---------------------- | ------------------------------------------------------------------------- |
| `ClusterSizeAligned` | `UpToDate`             | Число запущенных реплик соответствует запрошенной топологии               |
| `ClusterSizeAligned` | `ScalingUp`            | Оператор добавляет реплики                                                |
| `ClusterSizeAligned` | `ScalingDown`          | Оператор удаляет реплики                                                  |
| `SchemaInSync`       | `ReplicasInSync`       | Базы данных есть на всех репликах, а устаревшие метаданные удалены        |
| `SchemaInSync`       | `DatabasesNotCreated`  | Оператор еще не завершил создание баз данных на новых репликах            |
| `SchemaInSync`       | `ReplicasNotCleanedUp` | Устаревшие метаданные реплик после уменьшения числа реплик еще не удалены |
| `SchemaInSync`       | `SchemaSyncDisabled`   | `enableDatabaseSync` имеет значение `false`                               |
| `Ready`              | `AllShardsReady`       | В каждом сегменте есть готовая реплика                                    |
| `Ready`              | `SomeShardsNotReady`   | Как минимум в одном сегменте нет готовой реплики                          |

Операция масштабирования считается завершенной, когда `ClusterSizeAligned` имеет значение `UpToDate`, `SchemaInSync` — `ReplicasInSync`, а `Ready` — `AllShardsReady`.

<div id="scaling-keeper">
  ## Масштабирование Keeper
</div>

`KeeperCluster` работает с RAFT-кворумом, поэтому оператор изменяет его состав **по одной реплике за раз** и только когда кластер находится в стабильном состоянии. Это защищает кворум: кластер `2F+1` допускает отказ `F` участников, поэтому кластер из 3 узлов продолжает работать при отсутствии одного участника, а кластер из 5 узлов — двух.

```yaml theme={null}
spec:
  replicas: 5   # was 3
```

При масштабировании вверх оператор добавляет в кворум реплику с наименьшим свободным ID; при масштабировании вниз — удаляет реплику с наибольшим ID. На каждом шаге оператор ждёт, пока кворум стабилизируется, прежде чем начинать следующий. Для [Keeper PodDisruptionBudget](/docs/ru/products/kubernetes-operator/guides/configuration#pod-disruption-budgets) по умолчанию задано `maxUnavailable: replicas/2`, чтобы сохранять кворум во время плановых прерываний.

Условие `ScaleAllowed` показывает, может ли кворум изменить состав прямо сейчас:

| Причина                    | Значение                                                           |
| -------------------------- | ------------------------------------------------------------------ |
| `ReadyToScale`             | Кворум стабилен, и оператор может добавить или удалить участника   |
| `ReplicaHasPendingChanges` | У реплики всё ещё есть ожидающее применения изменение конфигурации |
| `ReplicaNotReady`          | Реплика не готова, поэтому изменение состава откладывается         |
| `NoQuorum`                 | В кластере нет кворума, и безопасно изменить состав нельзя         |
| `WaitingFollowers`         | Оператор ждёт, пока followers догонят лидера                       |

Масштабируйте Keeper по одному шагу и давайте `ScaleAllowed` вернуться к `ReadyToScale` между изменениями. Переход сразу на несколько участников не отменяет пошаговое согласование — оператор всё равно изменяет кворум по одному участнику за шаг.
