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

# Оптимизация производительности вставки и чтения для S3

> Оптимизация производительности чтения и вставки для S3

export const Image = ({img, alt, size = "lg"}) => {
  const normalizedSize = ["sm", "md", "lg"].includes(size) ? size : "lg";
  return <div className={`ch-image-${normalizedSize}`}>
      <Frame>
        <img src={img} alt={alt} />
      </Frame>
    </div>;
};

Этот раздел посвящён оптимизации производительности при чтении и вставке данных из S3 с помощью [табличных функций s3](/docs/ru/reference/functions/table-functions/s3).

<Info>
  **Описанный в этом руководстве подход можно применять и к другим реализациям объектного хранилища с собственными специализированными табличными функциями, таким как [GCS](/docs/ru/reference/functions/table-functions/gcs) и [Azure Blob storage](/docs/ru/reference/functions/table-functions/azureBlobStorage).**
</Info>

Прежде чем настраивать число потоков и размер блоков для повышения производительности вставки, рекомендуем разобраться в механике вставки в S3. Если вы уже с ней знакомы или просто хотите получить несколько быстрых рекомендаций, перейдите к примеру [ниже](/docs/ru/integrations/connectors/data-ingestion/AWS/performance#example-dataset).

<div id="insert-mechanics-single-node">
  ## Механика вставки (один узел)
</div>

Помимо характеристик оборудования, на производительность и потребление ресурсов при вставке данных в ClickHouse (на одном узле) влияют два основных фактора: **размер блока вставки** и **параллелизм вставки**.

<div id="insert-block-size">
  ### Размер блока вставки
</div>

<Image img="https://mintcdn.com/private-7c7dfe99/pIetLsS_hOGHqoPJ/images/integrations/data-ingestion/s3/insert_mechanics.webp?fit=max&auto=format&n=pIetLsS_hOGHqoPJ&q=85&s=6adff25cfa9fd14a3c81f461554830f1" size="lg" border alt="Механика размера блока вставки в ClickHouse" width="2862" height="964" data-path="images/integrations/data-ingestion/s3/insert_mechanics.webp" />

При выполнении `INSERT INTO SELECT` ClickHouse получает некоторую порцию данных и ① формирует из неё (как минимум) один блок вставки в памяти (для каждого [ключа партиционирования](/docs/ru/reference/engines/table-engines/mergetree-family/custom-partitioning-key)). Данные в блоке сортируются, и к ним применяются оптимизации, специфичные для движка таблицы. Затем данные сжимаются и ② записываются в хранилище базы данных в виде новой части данных.

Размер блока вставки влияет как на [дисковый файловый ввод-вывод](https://en.wikipedia.org/wiki/Category:Disk_file_systems), так и на потребление памяти сервером ClickHouse. Более крупные блоки вставки требуют больше памяти, но создают более крупные и менее многочисленные исходные части. Чем меньше частей ClickHouse нужно создать для загрузки большого объёма данных, тем меньше требуется дискового ввода-вывода и автоматических [фоновых слияний](https://clickhouse.com/blog/supercharge-your-clickhouse-data-loads-part1#more-parts--more-background-part-merges).

При использовании запроса `INSERT INTO SELECT` в сочетании с движком интеграционной таблицы или табличной функцией данные загружаются сервером ClickHouse:

<Image img="https://mintcdn.com/private-7c7dfe99/pIetLsS_hOGHqoPJ/images/integrations/data-ingestion/s3/pull.webp?fit=max&auto=format&n=pIetLsS_hOGHqoPJ&q=85&s=1f1515c79b625638804d437bf648aadc" size="lg" border alt="Загрузка данных из внешних источников в ClickHouse" width="3468" height="1168" data-path="images/integrations/data-ingestion/s3/pull.webp" />

Пока данные не будут полностью загружены, сервер выполняет цикл:

```bash theme={null}
① Получить и разобрать следующую порцию данных и сформировать из неё блок данных в памяти (по одному на ключ партиционирования).

② Записать блок в новую часть на хранилище.

Перейти к ① 
```

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

* [`min_insert_block_size_rows`](/docs/ru/reference/settings/session-settings#min_insert_block_size_rows) (по умолчанию: `1048545` строк)
* [`min_insert_block_size_bytes`](/docs/ru/reference/settings/session-settings#min_insert_block_size_bytes) (по умолчанию: `256 MiB`)

Когда в блоке вставки набирается либо указанное количество строк, либо достигается заданный объём данных (в зависимости от того, что произойдёт раньше), блок записывается в новую часть. Цикл вставки продолжается с шага ①.

Обратите внимание, что значение `min_insert_block_size_bytes` обозначает размер несжатого блока в памяти (а не размер сжатой части на диске). Также обратите внимание, что созданные блоки и части редко содержат в точности заданное количество строк или байтов, поскольку ClickHouse [обрабатывает](https://clickhouse.com/company/events/query-performance-introspection) данные в потоковом режиме, построчно-[блоками](/docs/ru/reference/settings/session-settings#max_block_size). Поэтому эти настройки задают минимальные пороговые значения.

<div id="be-aware-of-merges">
  #### Учитывайте слияния
</div>

Чем меньше заданный размер блока вставки, тем больше исходных частей создаётся при большой загрузке данных и тем больше фоновых слияний частей выполняется одновременно с приёмом данных. Это может приводить к конкуренции за ресурсы (CPU и память) и требовать дополнительного времени после завершения загрузки, чтобы число частей вернулось к [приемлемому](/docs/ru/reference/settings/merge-tree-settings#parts_to_throw_insert) уровню (3000).

<Warning>
  Производительность запросов ClickHouse снизится, если количество частей превысит [рекомендуемые пределы](/docs/ru/reference/settings/merge-tree-settings#parts_to_throw_insert).
</Warning>

ClickHouse будет непрерывно [сливать части](https://clickhouse.com/blog/asynchronous-data-inserts-in-clickhouse#data-needs-to-be-batched-for-optimal-performance) в более крупные, пока они не [достигнут](/docs/ru/reference/settings/merge-tree-settings#max_bytes_to_merge_at_max_space_in_pool) сжатого размера около 150 GiB. Эта диаграмма показывает, как сервер ClickHouse сливает части:

<Image img="https://mintcdn.com/private-7c7dfe99/pIetLsS_hOGHqoPJ/images/integrations/data-ingestion/s3/merges.webp?fit=max&auto=format&n=pIetLsS_hOGHqoPJ&q=85&s=023e482abfccfbb41d30e29d2d058326" size="lg" border alt="Фоновые слияния в ClickHouse" width="2512" height="2004" data-path="images/integrations/data-ingestion/s3/merges.webp" />

Один сервер ClickHouse использует несколько [фоновых потоков слияния](/docs/ru/reference/settings/server-settings/settings#background_pool_size) для выполнения параллельных [слияний частей](https://clickhouse.com/blog/supercharge-your-clickhouse-data-loads-part1#more-parts--more-background-part-merges:~:text=to%20execute%20concurrent-,part%20merges,-.%20Each%20thread%20executes). Каждый поток выполняет цикл:

```bash theme={null}
① Decide which parts to merge next, and load these parts as blocks into memory.

② Merge the loaded blocks in memory into a larger block.

③ Write the merged block into a new part on disk.

Go to ①
```

Обратите внимание, что [увеличение](https://clickhouse.com/blog/supercharge-your-clickhouse-data-loads-part1#hardware-size) числа ядер CPU и объема RAM повышает производительность фоновых слияний.

Части, слитые в более крупные, помечаются как [неактивные](/docs/ru/reference/system-tables/parts) и в итоге удаляются через [настраиваемое](/docs/ru/reference/settings/merge-tree-settings#old_parts_lifetime) количество минут. Со временем это формирует дерево слитых частей (отсюда и название таблицы [`MergeTree`](/docs/ru/reference/engines/table-engines/mergetree-family/index)).

<div id="insert-parallelism">
  ### Параллелизм вставки
</div>

<Image img="https://mintcdn.com/private-7c7dfe99/pIetLsS_hOGHqoPJ/images/integrations/data-ingestion/s3/resource_usage.webp?fit=max&auto=format&n=pIetLsS_hOGHqoPJ&q=85&s=5679e80ffd6dde3974235c2bd957281c" size="lg" border alt="Использование ресурсов при параллелизме вставки" width="2268" height="1562" data-path="images/integrations/data-ingestion/s3/resource_usage.webp" />

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

Табличные функции, такие как s3, позволяют задавать наборы имён файлов для загрузки с помощью glob-шаблонов. Если glob-шаблон соответствует нескольким существующим файлам, ClickHouse может распараллеливать чтение как между этими файлами, так и внутри них, а также параллельно вставлять данные в таблицу, используя одновременно работающие потоки вставки (на сервер):

<Image img="https://mintcdn.com/private-7c7dfe99/pIetLsS_hOGHqoPJ/images/integrations/data-ingestion/s3/insert_threads.webp?fit=max&auto=format&n=pIetLsS_hOGHqoPJ&q=85&s=95783ce64b7f31caa4a5ffbd60c6067a" size="lg" border alt="Параллельные потоки вставки в ClickHouse" width="2264" height="1316" data-path="images/integrations/data-ingestion/s3/insert_threads.webp" />

Пока не будут обработаны все данные из всех файлов, каждый поток вставки выполняет следующий цикл:

```bash theme={null}
① Get the next portion of unprocessed file data (portion size is based on the configured block size) and create an in-memory data block from it.

② Write the block into a new part on storage.

Go to ①. 
```

Количество таких параллельных потоков вставки можно настроить с помощью параметра [`max_insert_threads`](/docs/ru/reference/settings/session-settings#max_insert_threads). Значение по умолчанию — `1` для open-source ClickHouse и 4 для [ClickHouse Cloud](https://clickhouse.com/cloud).

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

Для функции и таблицы s3 параллельная загрузка отдельного файла определяется значениями [max\_download\_threads](/docs/ru/operations/settings/settings#max_download_threads) и [max\_download\_buffer\_size](/docs/ru/operations/settings/settings#max_download_buffer_size). Файлы будут загружаться параллельно, только если их размер превышает `2 * max_download_buffer_size`. По умолчанию значение `max_download_buffer_size` равно 10 MiB. В некоторых случаях можно без риска увеличить размер этого буфера до 50 MB (`max_download_buffer_size=52428800`), чтобы каждый файл загружался одним потоком. Это может сократить время, которое каждый поток тратит на обращения к S3, и тем самым уменьшить время ожидания S3. Кроме того, для файлов, слишком маленьких для параллельного чтения, ClickHouse для повышения пропускной способности автоматически выполняет предзагрузку данных, асинхронно читая такие файлы заранее.

<div id="measuring-performance">
  ## Измерение производительности
</div>

Оптимизация производительности запросов при использовании табличных функций S3 важна как при выполнении запросов непосредственно к данным, то есть при ad-hoc-запросах, когда используются только вычислительные ресурсы ClickHouse, а данные остаются в S3 в исходном формате, так и при вставке данных из S3 в таблицу ClickHouse на движке MergeTree. Если не указано иное, следующие рекомендации относятся к обоим сценариям.

<div id="impact-of-hardware-size">
  ## Влияние конфигурации оборудования
</div>

<Image img="https://mintcdn.com/private-7c7dfe99/pIetLsS_hOGHqoPJ/images/integrations/data-ingestion/s3/hardware_size.webp?fit=max&auto=format&n=pIetLsS_hOGHqoPJ&q=85&s=c75372efe3128b8193b43c003979084e" size="lg" border alt="Влияние конфигурации оборудования на производительность ClickHouse" width="2748" height="1894" data-path="images/integrations/data-ingestion/s3/hardware_size.webp" />

Количество доступных ядер процессора и объём оперативной памяти влияют на:

* поддерживаемый [начальный размер частей](#insert-block-size)
* возможный уровень [параллелизма вставки](#insert-parallelism)
* производительность [фоновых слияний частей](https://clickhouse.com/blog/supercharge-your-clickhouse-data-loads-part1#more-parts--more-background-part-merges)

и, следовательно, на общую скорость загрузки данных.

<div id="region-locality">
  ## Один и тот же регион
</div>

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

<div id="formats">
  ## Форматы
</div>

ClickHouse может читать файлы, хранящиеся в бакетах S3, в [поддерживаемых форматах](/docs/ru/reference/formats/index#formats-overview), используя функцию `s3` и движок `S3`. При чтении сырых файлов некоторые из этих форматов имеют явные преимущества:

* Форматы с закодированными именами столбцов, такие как Native, Parquet, CSVWithNames и TabSeparatedWithNames, делают запросы менее многословными, поскольку пользователю не нужно указывать имена столбцов в функции `s3`. Эта информация определяется автоматически по именам столбцов.
* Форматы различаются по производительности чтения и записи. Native и Parquet — наиболее оптимальные форматы для чтения, поскольку они изначально являются колоночными и более компактны. Формат Native дополнительно выигрывает за счет того, что соответствует тому, как ClickHouse хранит данные в памяти, — это снижает накладные расходы на обработку при потоковой загрузке данных в ClickHouse.
* Размер блока часто влияет на задержку при чтении больших файлов. Это особенно заметно, если вам нужна лишь выборка данных, например первые N строк. В случае форматов вроде CSV и TSV, чтобы вернуть набор строк, файл необходимо полностью разобрать. Форматы вроде Native и Parquet позволяют делать такую выборку быстрее.
* У каждого формата сжатия есть свои плюсы и минусы: обычно это компромисс между степенью сжатия и скоростью, с акцентом либо на производительность сжатия, либо на скорость распаковки. Если вы сжимаете сырые файлы, такие как CSV или TSV, lz4 обеспечивает самую быструю распаковку, жертвуя степенью сжатия. Gzip обычно сжимает лучше, но скорость чтения при этом немного ниже. Xz идет еще дальше и обычно обеспечивает наилучшее сжатие, но при самой низкой скорости сжатия и распаковки. При экспорте Gz и lz4 дают сопоставимую скорость сжатия. Соотносите это со скоростью вашего соединения. Любой выигрыш от более быстрой распаковки или сжатия легко может быть сведен на нет медленным соединением с вашими бакетами S3.
* Такие форматы, как Native или Parquet, обычно не оправдывают дополнительные затраты на сжатие. Экономия места, скорее всего, будет минимальной, поскольку эти форматы сами по себе компактны. Время, затраченное на сжатие и распаковку, редко компенсирует время передачи по сети — особенно с учетом того, что S3 доступен по всему миру и обычно обеспечивает высокую пропускную способность.

<div id="example-dataset">
  ## Пример набора данных
</div>

Чтобы нагляднее показать возможные оптимизации, мы будем использовать [посты из набора данных Stack Overflow](/docs/ru/guides/clickhouse/data-modelling/schema-design#stack-overflow-dataset) и оптимизировать для этих данных как производительность запросов, так и скорость вставки.

Этот набор данных состоит из 189 файлов Parquet — по одному на каждый месяц с июля 2008 года по март 2024 года.

Обратите внимание, что для повышения производительности мы используем Parquet в соответствии с [нашими рекомендациями выше](#formats) и выполняем все запросы на кластере ClickHouse, расположенном в том же регионе, что и бакет. Этот кластер состоит из 3 узлов, каждый из которых имеет 32 GiB RAM и 8 vCPU.

Без какой-либо дополнительной настройки мы покажем производительность вставки этого набора данных в таблицу на движке MergeTree, а также выполнения запроса для определения пользователей, задающих больше всего вопросов. Оба этих запроса намеренно требуют полного сканирования данных.

```sql theme={null}
-- Топ пользователей по именам
SELECT
    OwnerDisplayName,
    count() AS num_posts
FROM s3('https://datasets-documentation.s3.eu-west-3.amazonaws.com/stackoverflow/parquet/posts/by_month/*.parquet')
WHERE OwnerDisplayName NOT IN ('', 'anon')
GROUP BY OwnerDisplayName
ORDER BY num_posts DESC
LIMIT 5
```

```response theme={null}
┌─OwnerDisplayName─┬─num_posts─┐
│ user330315       │     10344 │
│ user4039065      │      5316 │
│ user149341       │      4102 │
│ user529758       │      3700 │
│ user3559349      │      3068 │
└──────────────────┴───────────┘

5 rows in set. Elapsed: 3.013 sec. Processed 59.82 million rows, 24.03 GB (19.86 million rows/s., 7.98 GB/s.)
Peak memory usage: 603.64 MiB.
```

```sql theme={null}
-- Загрузка в таблицу posts
INSERT INTO posts SELECT *
FROM s3('https://datasets-documentation.s3.eu-west-3.amazonaws.com/stackoverflow/parquet/posts/by_month/*.parquet')
```

```response theme={null}
0 rows in set. Elapsed: 191.692 sec. Processed 59.82 million rows, 24.03 GB (312.06 thousand rows/s., 125.37 MB/s.)
```

В нашем примере мы возвращаем лишь несколько строк. Если вы измеряете производительность запросов `SELECT`, которые возвращают клиенту большие объёмы данных, используйте для запросов либо [формат `Null`](/docs/ru/reference/formats/Null), либо направляйте результаты в движок [`Null`](/docs/ru/reference/engines/table-engines/special/null). Это поможет избежать перегрузки клиента данными и насыщения сети.

<Info>
  При выполнении запросов на чтение первый запрос часто может казаться медленнее, чем повторное выполнение того же запроса. Это может быть связано как с собственным кэшированием S3, так и с [кэшем вывода схемы ClickHouse](/docs/ru/reference/system-tables/schema_inference_cache). В нём сохраняется выведенная схема файлов, благодаря чему при последующих обращениях можно пропустить этап вывода схемы и тем самым сократить время выполнения запроса.
</Info>

<div id="using-threads-for-reads">
  ## Использование потоков для чтения
</div>

Производительность чтения из S3 будет линейно расти с увеличением числа ядер, если только вы не упираетесь в пропускную способность сети или локального I/O. Увеличение числа потоков также ведет к дополнительным затратам памяти, о которых следует помнить. Для потенциального повышения пропускной способности чтения можно изменить следующее:

* Обычно значения `max_threads` по умолчанию достаточно, то есть числа ядер. Если запрос потребляет много памяти и это нужно сократить, либо если `LIMIT` для результатов невелик, это значение можно уменьшить. Пользователи с большим объемом памяти могут поэкспериментировать с увеличением этого значения, чтобы, возможно, добиться более высокой пропускной способности чтения из S3. Как правило, это полезно только на машинах с небольшим числом ядер, то есть \< 10. Выигрыш от дальнейшей параллелизации обычно снижается, поскольку другие ресурсы становятся узким местом, например сеть и конкуренция за CPU.
* Версии ClickHouse до 22.3.1 распараллеливали чтение только между несколькими файлами при использовании функции `s3` или табличного движка `S3`. Это требовало от пользователя разбивать файлы на части в S3 и читать их с использованием glob-шаблона, чтобы добиться оптимальной производительности чтения. В более поздних версиях загрузки теперь распараллеливаются и внутри одного файла.
* В сценариях с небольшим числом потоков может помочь установка `remote_filesystem_read_method` в значение "read", чтобы включить синхронное чтение файлов из S3.
* Для функции `s3` и таблицы параллельная загрузка отдельного файла определяется значениями [`max_download_threads`](/docs/ru/reference/settings/session-settings#max_download_threads) и [`max_download_buffer_size`](/docs/ru/reference/settings/session-settings#max_download_buffer_size). Хотя [`max_download_threads`](/docs/ru/reference/settings/session-settings#max_download_threads) управляет числом используемых потоков, файлы будут загружаться параллельно только в том случае, если их размер превышает 2 \* `max_download_buffer_size`. По умолчанию значение `max_download_buffer_size` равно 10MiB. В некоторых случаях можно безопасно увеличить размер этого буфера до 50 MB (`max_download_buffer_size=52428800`), чтобы небольшие файлы загружались только одним потоком. Это может сократить время, которое каждый поток тратит на вызовы S3, и тем самым уменьшить время ожидания S3. Пример см. в [этой записи блога](https://clickhouse.com/blog/clickhouse-1-trillion-row-challenge).

Прежде чем вносить какие-либо изменения для повышения производительности, обязательно проведите соответствующие замеры. Поскольку вызовы API S3 чувствительны к задержке и могут влиять на время выполнения на стороне клиента, используйте для метрик производительности журнал запросов, то есть `system.query_log`.

Рассмотрим наш предыдущий запрос: удвоение `max_threads` до `16` (значение `max_thread` по умолчанию равно числу ядер на узле) повышает производительность запроса на чтение в 2 раза ценой большего потребления памяти. Дальнейшее увеличение `max_threads` дает все меньший эффект, как показано.

```sql theme={null}
SELECT
    OwnerDisplayName,
    count() AS num_posts
FROM s3('https://datasets-documentation.s3.eu-west-3.amazonaws.com/stackoverflow/parquet/posts/by_month/*.parquet')
WHERE OwnerDisplayName NOT IN ('', 'anon')
GROUP BY OwnerDisplayName
ORDER BY num_posts DESC
LIMIT 5
SETTINGS max_threads = 16
```

```response theme={null}
┌─OwnerDisplayName─┬─num_posts─┐
│ user330315       │     10344 │
│ user4039065      │      5316 │
│ user149341       │      4102 │
│ user529758       │      3700 │
│ user3559349      │      3068 │
└──────────────────┴───────────┘

5 rows in set. Elapsed: 1.505 sec. Processed 59.82 million rows, 24.03 GB (39.76 million rows/s., 15.97 GB/s.)
Peak memory usage: 178.58 MiB.
```

```sql theme={null}
SETTINGS max_threads = 32
```

```response theme={null}
5 rows in set. Elapsed: 0.779 sec. Processed 59.82 million rows, 24.03 GB (76.81 million rows/s., 30.86 GB/s.)
Peak memory usage: 369.20 MiB.
```

```sql theme={null}
SETTINGS max_threads = 64
```

```response theme={null}
5 rows in set. Elapsed: 0.674 sec. Processed 59.82 million rows, 24.03 GB (88.81 million rows/s., 35.68 GB/s.)
Peak memory usage: 639.99 MiB.
```

<div id="tuning-threads-and-block-size-for-inserts">
  ## Настройка потоков и размера блоков для вставки
</div>

Чтобы добиться максимальной производительности загрузки, необходимо выбрать (1) размер блока вставки и (2) подходящий уровень параллелизма вставки с учётом (3) количества доступных ядер CPU и объёма доступной RAM. Вкратце:

* Чем больше [размер блока вставки](#insert-block-size), тем меньше частей ClickHouse приходится создавать и тем меньше требуется [дисковых операций ввода-вывода](https://en.wikipedia.org/wiki/Category:Disk_file_systems) и [фоновых слияний](https://clickhouse.com/blog/supercharge-your-clickhouse-data-loads-part1#more-parts--more-background-part-merges).
* Чем выше [число параллельных потоков вставки](#insert-parallelism), тем быстрее будут обрабатываться данные.

Между этими двумя факторами производительности существует компромисс (как и компромисс с фоновым слиянием частей). Объём доступной оперативной памяти на серверах ClickHouse ограничен. Более крупные блоки занимают больше оперативной памяти, а это ограничивает количество параллельных потоков вставки, которые можно задействовать. И наоборот, большее число параллельных потоков вставки требует больше оперативной памяти, поскольку именно число потоков вставки определяет, сколько блоков вставки одновременно создаётся в памяти. Это, в свою очередь, ограничивает возможный размер блоков вставки. Кроме того, между потоками вставки и потоками фонового слияния может возникать конкуренция за ресурсы. Большое число настроенных потоков вставки (1) создаёт больше частей, которые затем нужно сливать, и (2) отнимает у потоков фонового слияния ядра CPU и оперативную память.

Подробное описание того, как эти параметры влияют на производительность и потребление ресурсов, рекомендуем [прочитать в этом посте блога](https://clickhouse.com/blog/supercharge-your-clickhouse-data-loads-part2). Как показано в этом посте, настройка может потребовать тщательного подбора баланса между этими двумя параметрами. Такое всестороннее тестирование часто оказывается непрактичным, поэтому в качестве краткой рекомендации мы советуем:

```bash theme={null}
• max_insert_threads: выберите ~ половину доступных ядер CPU для потоков вставки (чтобы оставить достаточно ядер для фоновых слияний)

• peak_memory_usage_in_bytes: выберите планируемый пиковый объём потребления памяти: либо весь доступный RAM (если загрузка данных выполняется изолированно), либо половину или меньше (чтобы оставить ресурсы для других параллельных задач)

Затем:
min_insert_block_size_bytes = peak_memory_usage_in_bytes / (~3 * max_insert_threads)
```

С помощью этой формулы можно установить `min_insert_block_size_rows` в 0 (чтобы отключить порог по числу строк), задать `max_insert_threads` нужным значением, а `min_insert_block_size_bytes` — значением, вычисленным по приведённой выше формуле.

Применим эту формулу к нашему предыдущему примеру со Stack Overflow.

* `max_insert_threads=4` (8 ядер на узел)
* `peak_memory_usage_in_bytes` — 32 GiB (100% ресурсов узла) или `34359738368` байт.
* `min_insert_block_size_bytes` = `34359738368/(3*4) = 2863311530`

```sql theme={null}
INSERT INTO posts SELECT *
FROM s3('https://datasets-documentation.s3.eu-west-3.amazonaws.com/stackoverflow/parquet/posts/by_month/*.parquet') SETTINGS min_insert_block_size_rows=0, max_insert_threads=4, min_insert_block_size_bytes=2863311530
```

```response theme={null}
0 rows in set. Elapsed: 128.566 sec. Processed 59.82 million rows, 24.03 GB (465.28 thousand rows/s., 186.92 MB/s.)
```

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

<div id="scaling-with-resources-and-nodes">
  ## Масштабирование за счёт ресурсов и узлов
</div>

Масштабирование за счёт ресурсов и узлов применимо как к запросам на чтение, так и к запросам на вставку.

<div id="vertical-scaling">
  ### Вертикальное масштабирование
</div>

Во всех предыдущих настройках и запросах использовался только один узел в нашем кластере ClickHouse Cloud. При этом в вашем распоряжении нередко будет несколько узлов ClickHouse. Мы рекомендуем сначала использовать вертикальное масштабирование, поскольку пропускная способность S3 линейно растет с увеличением числа ядер. Если повторить наши предыдущие запросы на вставку и чтение на более крупном узле ClickHouse Cloud с удвоенными ресурсами (64GiB, 16 vCPUs) и соответствующими настройками, оба запроса будут выполняться примерно в два раза быстрее.

```sql theme={null}
INSERT INTO posts SELECT *
FROM s3('https://datasets-documentation.s3.eu-west-3.amazonaws.com/stackoverflow/parquet/posts/by_month/*.parquet') SETTINGS min_insert_block_size_rows=0, max_insert_threads=8, min_insert_block_size_bytes=2863311530
```

```response theme={null}
0 rows in set. Elapsed: 67.294 sec. Processed 59.82 million rows, 24.03 GB (888.93 thousand rows/s., 357.12 MB/s.)
```

```sql theme={null}
SELECT
    OwnerDisplayName,
    count() AS num_posts
FROM s3('https://datasets-documentation.s3.eu-west-3.amazonaws.com/stackoverflow/parquet/posts/by_month/*.parquet')
WHERE OwnerDisplayName NOT IN ('', 'anon')
GROUP BY OwnerDisplayName
ORDER BY num_posts DESC
LIMIT 5
SETTINGS max_threads = 92
```

```response theme={null}
5 rows in set. Elapsed: 0.421 sec. Processed 59.82 million rows, 24.03 GB (142.08 million rows/s., 57.08 GB/s.)
```

<Note>
  Производительность отдельных узлов также может упираться в ограничения сети и S3 GET-запросов, что не позволяет добиваться линного прироста производительности при вертикальном масштабировании.
</Note>

<div id="horizontal-scaling">
  ### Горизонтальное масштабирование
</div>

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

Чтобы использовать кластер при чтении из S3, нужно применять функцию `s3Cluster`, как описано в разделе [Использование кластеров](/docs/ru/integrations/connectors/data-ingestion/AWS/integrating-s3-with-clickhouse#utilizing-clusters). Это позволяет распределять чтение между узлами.

Сервер, который первым получает запрос на вставку, сначала разворачивает glob-шаблон, а затем динамически распределяет обработку каждого подходящего файла между собой и другими серверами.

<Image img="https://mintcdn.com/private-7c7dfe99/PWQnWTwcu17exYX2/images/integrations/data-ingestion/s3/s3Cluster.webp?fit=max&auto=format&n=PWQnWTwcu17exYX2&q=85&s=f9742e864565959d134315dfd6ff3bf2" size="lg" border alt="функция s3Cluster в ClickHouse" width="2746" height="2000" data-path="images/integrations/data-ingestion/s3/s3Cluster.webp" />

Повторим предыдущий запрос на чтение, распределив нагрузку между 3 узлами и изменив запрос так, чтобы использовать `s3Cluster`. В ClickHouse Cloud это делается автоматически — достаточно указать кластер `default`.

Как отмечено в разделе [Использование кластеров](/docs/ru/integrations/connectors/data-ingestion/AWS/integrating-s3-with-clickhouse#utilizing-clusters), эта работа распределяется на уровне файлов. Чтобы воспользоваться этой возможностью, потребуется достаточное количество файлов, то есть как минимум больше, чем число узлов.

```sql theme={null}
SELECT
    OwnerDisplayName,
    count() AS num_posts
FROM s3Cluster('default', 'https://datasets-documentation.s3.eu-west-3.amazonaws.com/stackoverflow/parquet/posts/by_month/*.parquet')
WHERE OwnerDisplayName NOT IN ('', 'anon')
GROUP BY OwnerDisplayName
ORDER BY num_posts DESC
LIMIT 5
SETTINGS max_threads = 16
```

```response theme={null}
┌─OwnerDisplayName─┬─num_posts─┐
│ user330315       │     10344 │
│ user4039065      │      5316 │
│ user149341       │      4102 │
│ user529758       │      3700 │
│ user3559349      │      3068 │
└──────────────────┴───────────┘

5 rows in set. Elapsed: 0.622 sec. Processed 59.82 million rows, 24.03 GB (96.13 million rows/s., 38.62 GB/s.)
Peak memory usage: 176.74 MiB.
```

Аналогично, наш запрос INSERT можно выполнять в распределённом режиме, используя улучшенные настройки, ранее определённые для одного узла:

```sql theme={null}
INSERT INTO posts SELECT *
FROM s3Cluster('default', 'https://datasets-documentation.s3.eu-west-3.amazonaws.com/stackoverflow/parquet/posts/by_month/*.parquet') SETTINGS min_insert_block_size_rows=0, max_insert_threads=4, min_insert_block_size_bytes=2863311530
```

```response theme={null}
0 rows in set. Elapsed: 171.202 sec. Processed 59.82 million rows, 24.03 GB (349.41 thousand rows/s., 140.37 MB/s.)
```

Читатели заметят, что чтение файлов улучшило производительность запросов, но не вставки. По умолчанию, хотя операции чтения распределяются с помощью `s3Cluster`, вставка выполняется через узел-инициатор. Это означает, что, хотя чтение происходит на каждом узле, полученные строки для распределения направляются на узел-инициатор. В сценариях с высокой пропускной способностью это может стать узким местом. Чтобы решить эту проблему, задайте параметр `parallel_distributed_insert_select` для функции `s3cluster`.

Значение `parallel_distributed_insert_select=2` гарантирует, что `SELECT` и `INSERT` будут выполняться на каждом шарде в базовую таблицу распределённого движка и из неё на каждом узле.

```sql theme={null}
INSERT INTO posts
SELECT *
FROM s3Cluster('default', 'https://datasets-documentation.s3.eu-west-3.amazonaws.com/stackoverflow/parquet/posts/by_month/*.parquet')
SETTINGS parallel_distributed_insert_select = 2, min_insert_block_size_rows=0, max_insert_threads=4, min_insert_block_size_bytes=2863311530
```

```response theme={null}
0 rows in set. Elapsed: 54.571 sec. Processed 59.82 million rows, 24.03 GB (1.10 million rows/s., 440.38 MB/s.)
Peak memory usage: 11.75 GiB.
```

Как и ожидалось, это снижает производительность вставки втрое.

<div id="further-tuning">
  ## Дополнительная настройка
</div>

<div id="disable-de-duplication">
  ### Отключение дедупликации
</div>

Операции вставки иногда могут завершаться ошибкой, например из-за тайм-аутов. В таких случаях данные могли как успешно записаться, так и не записаться. Чтобы клиент мог безопасно повторить попытку вставки, в распределённых развёртываниях, таких как ClickHouse Cloud, ClickHouse по умолчанию пытается определить, были ли данные уже успешно вставлены. Если вставленные данные помечаются как дубликат, ClickHouse не вставляет их в целевую таблицу. Однако пользователь всё равно получит статус успешного выполнения операции — как если бы данные были вставлены обычным образом.

Хотя такое поведение, создающее дополнительную нагрузку при вставке, оправдано при загрузке данных с клиента или пакетами, оно может быть излишним при выполнении `INSERT INTO SELECT` из объектного хранилища. Если отключить эту функциональность во время вставки, можно повысить производительность, как показано ниже:

```sql theme={null}
INSERT INTO posts
SETTINGS parallel_distributed_insert_select = 2, min_insert_block_size_rows = 0, max_insert_threads = 4, min_insert_block_size_bytes = 2863311530, insert_deduplicate = 0
SELECT *
FROM s3Cluster('default', 'https://datasets-documentation.s3.eu-west-3.amazonaws.com/stackoverflow/parquet/posts/by_month/*.parquet')
SETTINGS parallel_distributed_insert_select = 2, min_insert_block_size_rows = 0, max_insert_threads = 4, min_insert_block_size_bytes = 2863311530, insert_deduplicate = 0
```

```response theme={null}
0 rows in set. Elapsed: 52.992 sec. Processed 59.82 million rows, 24.03 GB (1.13 million rows/s., 453.50 MB/s.)
Peak memory usage: 26.57 GiB.
```

<div id="optimize-on-insert">
  ### Оптимизация при вставке
</div>

В ClickHouse настройка `optimize_on_insert` определяет, будут ли части данных сливаться в процессе вставки. Когда она включена (`optimize_on_insert = 1` по умолчанию), небольшие части сливаются в более крупные по мере вставки, что повышает производительность запросов за счёт сокращения числа частей, которые нужно читать. Однако такое слияние создаёт дополнительную нагрузку на процесс вставки и может замедлить вставки с высокой пропускной способностью.

Отключение этой настройки (`optimize_on_insert = 0`) отключает слияние во время вставки, позволяя записывать данные быстрее, особенно при частых небольших вставках. Процесс слияния переносится в фоновый режим, что улучшает производительность вставки, но временно увеличивает количество небольших частей, из-за чего запросы могут выполняться медленнее, пока фоновое слияние не завершится. Эта настройка особенно полезна, когда в приоритете скорость вставки, а фоновый процесс слияния может позже эффективно выполнить оптимизацию. Как показано ниже, отключение этой настройки может повысить пропускную способность вставки:

```sql theme={null}
SELECT *
FROM s3Cluster('default', 'https://datasets-documentation.s3.eu-west-3.amazonaws.com/stackoverflow/parquet/posts/by_month/*.parquet')
SETTINGS parallel_distributed_insert_select = 2, min_insert_block_size_rows = 0, max_insert_threads = 4, min_insert_block_size_bytes = 2863311530, insert_deduplicate = 0, optimize_on_insert = 0
```

```response theme={null}
0 rows in set. Elapsed: 49.688 sec. Processed 59.82 million rows, 24.03 GB (1.20 million rows/s., 483.66 MB/s.)
```

<div id="misc-notes">
  ## Прочие замечания
</div>

* При нехватке памяти, если вы вставляете данные в S3, попробуйте уменьшить `max_insert_delayed_streams_for_parallel_write`.
