Этот раздел посвящён оптимизации производительности при чтении данных из S3 и их вставке с помощью табличных функций S3.
Описанный в этом руководстве материал применим и к другим Объектным хранилищам с собственными специализированными табличными функциями, таким как GCS и Azure Blob Storage.
Прежде чем настраивать число потоков и размеры блоков для повышения производительности вставки, мы рекомендуем разобраться в том, как работают вставки в S3. Если вы уже знакомы с этим механизмом или просто хотите получить несколько быстрых рекомендаций, перейдите к примеру ниже.
Механизм вставки (один узел)
На производительность и потребление ресурсов при вставке данных в ClickHouse (на одном узле), помимо аппаратных ресурсов, влияют два основных фактора: размер блока вставки и параллелизм вставки.
При выполнении INSERT INTO SELECT ClickHouse получает некоторую порцию данных и ① формирует из полученных данных (как минимум) один блок вставки в памяти (на каждый ключ партиционирования). Данные в блоке сортируются, и к ним применяются оптимизации, специфичные для движка таблицы. Затем данные сжимаются и ② записываются в хранилище базы данных в виде новой части данных.
Размер блока вставки влияет как на операции дискового ввода-вывода, так и на использование памяти сервера ClickHouse. Более крупные блоки вставки требуют больше памяти, но создают более крупные и менее многочисленные начальные части. Чем меньше частей ClickHouse нужно создать для загрузки большого объёма данных, тем меньше требуется операций дискового ввода-вывода и автоматических фоновых слияний.
При использовании запроса INSERT INTO SELECT в сочетании с интеграционным движком таблицы или табличной функцией данные извлекаются сервером ClickHouse:
Пока данные не будут полностью загружены, сервер выполняет цикл:
① Получить и разобрать следующую порцию данных и сформировать из неё блок данных в памяти (по одному на ключ партиционирования).
② Записать блок в новую часть на хранилище.
Перейти к ①
В ① размер зависит от размера блока вставки, который можно регулировать с помощью двух настроек:
Когда в блоке вставки накапливается либо указанное количество строк, либо заданный объём данных (в зависимости от того, что произойдёт раньше), блок записывается в новую часть. После этого цикл вставки продолжается с шага ①.
Обратите внимание, что значение min_insert_block_size_bytes обозначает размер несжатого блока в памяти (а не размер сжатой части на диске). Также учтите, что создаваемые блоки и части редко содержат ровно заданное количество строк или байтов, поскольку ClickHouse передаёт и обрабатывает данные блоками строк. Поэтому эти настройки задают минимальные пороги.
Чем меньше настроенный размер блока вставки, тем больше начальных частей создаётся при большой загрузке данных и тем больше фоновых слияний частей выполняется параллельно с ингестией данных. Это может привести к конкуренции за ресурсы (CPU и память), а после завершения ингестии может потребоваться дополнительное время, чтобы количество частей вернулось к нормальному значению (3000).
Производительность запросов к ClickHouse снизится, если количество частей превысит рекомендуемые пределы.
ClickHouse будет непрерывно сливать части в более крупные, пока они не достигнут сжатого размера примерно 150 GiB. Эта диаграмма показывает, как сервер ClickHouse сливает части:
Один сервер ClickHouse использует несколько потоков фоновых слияний для выполнения параллельных слияний частей. Каждый поток выполняет цикл:
① Определить, какие части объединить следующими, и загрузить эти части как блоки в память.
② Объединить загруженные блоки в памяти в один более крупный блок.
③ Записать слитый блок в новую часть на диске.
Перейти к ①
Обратите внимание, что увеличение количества ядер процессора и объема оперативной памяти повышает скорость фоновых слияний.
Части, объединенные в более крупные, помечаются как неактивные и в конечном итоге удаляются по истечении настраиваемого количества минут. Со временем это формирует дерево слитых частей (отсюда и название таблицы MergeTree).
Сервер ClickHouse может параллельно обрабатывать данные и выполнять их вставку. Уровень параллелизма вставки влияет на пропускную способность ингестии и использование памяти сервера ClickHouse. Параллельная загрузка и обработка данных требуют больше оперативной памяти, но повышают пропускную способность ингестии, поскольку данные обрабатываются быстрее.
Табличные функции, такие как s3, позволяют задавать наборы имён загружаемых файлов с помощью glob-шаблонов. Если glob-шаблон соответствует нескольким существующим файлам, ClickHouse может распараллеливать чтение как между этими файлами, так и внутри них, а также параллельно вставлять данные в таблицу, используя параллельно работающие потоки вставки (на каждом сервере):
Пока не будут обработаны все данные из всех файлов, каждый поток вставки выполняет цикл:
① Получить следующую порцию необработанных данных файла (размер порции определяется настроенным размером блока) и создать из неё блок данных в памяти.
② Записать блок в новую часть на хранилище.
Перейти к ①.
Количество таких параллельных потоков вставки можно настроить с помощью параметра max_insert_threads. Значение по умолчанию — 1 для ClickHouse с открытым исходным кодом и 4 для ClickHouse Cloud.
При большом количестве файлов параллельная обработка несколькими потоками вставки работает эффективно. Она может полностью загрузить как доступные ядра CPU, так и пропускную способность сети (при параллельной загрузке файлов). В сценариях, когда в таблицу загружается лишь несколько больших файлов, ClickHouse автоматически обеспечивает высокий уровень параллелизма при обработке данных и оптимизирует использование пропускной способности сети, создавая дополнительные потоки чтения для каждого потока вставки, чтобы параллельно читать (загружать) больше различных диапазонов внутри крупных файлов.
Для функции s3 и таблицы параллельная загрузка отдельного файла определяется значениями max_download_threads и max_download_buffer_size. Файлы будут загружаться параллельно только в том случае, если их размер превышает 2 * max_download_buffer_size. По умолчанию значение max_download_buffer_size установлено в 10MiB. В некоторых случаях можно без риска увеличить размер этого буфера до 50 MB (max_download_buffer_size=52428800), чтобы каждый файл загружался одним потоком. Это может сократить время, которое каждый поток тратит на вызовы S3, и тем самым также уменьшить время ожидания при обращении к S3. Кроме того, чтобы повысить пропускную способность при работе с файлами, которые слишком малы для параллельного чтения, ClickHouse автоматически выполняет предварительное чтение данных, асинхронно подчитывая такие файлы.
Оптимизация производительности запросов с использованием табличных функций S3 важна как при выполнении запросов к данным на месте, то есть при ad-hoc-запросах, когда используются только вычислительные ресурсы ClickHouse, а данные остаются в S3 в исходном формате, так и при вставке данных из S3 в таблицу ClickHouse с движком MergeTree. Если не указано иное, приведенные ниже рекомендации относятся к обоим сценариям.
Влияние размера аппаратных ресурсов
Количество доступных ядер процессора и объём оперативной памяти влияют на:
и, следовательно, на общую пропускную способность ингестии.
Убедитесь, что ваши бакеты находятся в том же регионе, что и инстансы ClickHouse. Эта простая оптимизация может заметно повысить пропускную способность, особенно если вы разворачиваете инстансы ClickHouse на инфраструктуре AWS.
ClickHouse может читать файлы, хранящиеся в S3 бакетах, в поддерживаемых форматах с помощью функции 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 глобально доступен и обычно обеспечивает высокую пропускную способность сети.
Чтобы продемонстрировать дальнейшие возможности оптимизации, мы будем использовать посты из набора данных Stack Overflow и оптимизировать как производительность запросов, так и производительность вставки этих данных.
Этот набор данных состоит из 189 файлов Parquet — по одному на каждый месяц с июля 2008 года по март 2024 года.
Обратите внимание, что для повышения производительности мы используем Parquet в соответствии с нашими рекомендациями выше и выполняем все запросы на кластере ClickHouse, расположенном в том же регионе, что и бакет. Этот кластер состоит из 3 узлов, каждый из которых имеет 32 GiB оперативной памяти и 8 vCPU.
Без какой-либо дополнительной настройки мы покажем производительность вставки этого набора данных в движок таблицы MergeTree, а также выполнения запроса, определяющего пользователей, задающих больше всего вопросов. Оба этих запроса намеренно требуют полного сканирования данных.
-- Топ имён пользователей
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
┌─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.
-- Загрузка в таблицу posts
INSERT INTO posts SELECT *
FROM s3('https://datasets-documentation.s3.eu-west-3.amazonaws.com/stackoverflow/parquet/posts/by_month/*.parquet')
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 или направляйте результаты в движок Null. Это позволит избежать перегрузки клиента данными и насыщения сети.
При выполнении запросов на чтение первый запрос часто оказывается медленнее, чем повторный запуск того же запроса. Это может быть связано как с собственным кэшированием S3, так и с кэшем автоопределения схемы ClickHouse. В нём хранится определённая схема файлов, поэтому при последующих обращениях этап определения схемы можно пропустить, что сокращает время выполнения запроса.
Использование потоков для чтения
Производительность чтения из 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 и max_download_buffer_size. Хотя max_download_threads управляет количеством используемых потоков, файлы будут загружаться параллельно только в том случае, если их размер превышает 2 * max_download_buffer_size. По умолчанию значение max_download_buffer_size равно 10 MiB. В некоторых случаях можно безопасно увеличить размер этого буфера до 50 MB (max_download_buffer_size=52428800), чтобы файлы меньшего размера загружались только одним потоком. Это может сократить время, которое каждый поток тратит на вызовы S3, и тем самым уменьшить время ожидания S3. Пример см. в этом посте блога.
Прежде чем вносить какие-либо изменения для повышения производительности, обязательно выполните корректные замеры. Поскольку вызовы API S3 чувствительны к задержке и могут влиять на тайминги клиента, используйте для оценки производительности журнал запросов, то есть system.query_log.
Вернемся к нашему предыдущему запросу: увеличение max_threads вдвое, до 16 (значение max_thread по умолчанию равно числу ядер на узле), повышает производительность запроса на чтение в 2 раза ценой большего потребления памяти. Дальнейшее увеличение max_threads дает убывающую отдачу, как показано ниже.
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
┌─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.
SETTINGS max_threads = 32
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.)
Пиковое потребление памяти: 369.20 MiB.
SETTINGS max_threads = 64
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.)
Пиковое потребление памяти: 639.99 MiB.
Настройка потоков и размера блока для вставок
Чтобы добиться максимальной производительности ингестии, необходимо выбрать (1) размер блока вставки и (2) подходящий уровень параллелизма вставки с учётом (3) количества доступных ядер CPU и объёма доступной оперативной памяти. Вкратце:
Между этими двумя факторами производительности существует компромисс (в дополнение к компромиссу, связанному с фоновым слиянием частей). Объём доступной оперативной памяти серверов ClickHouse ограничен. Более крупные блоки используют больше оперативной памяти, что ограничивает количество параллельных потоков вставки, которые можно задействовать. И наоборот, большее число параллельных потоков вставки требует больше оперативной памяти, поскольку количество потоков вставки определяет число блоков вставки, одновременно создаваемых в памяти. Это, в свою очередь, ограничивает возможный размер блоков вставки. Кроме того, между потоками вставки и потоками фонового слияния может возникать конкуренция за ресурсы. Большое число настроенных потоков вставки (1) создаёт больше частей, которые нужно сливать, и (2) отнимает у потоков фонового слияния ядра CPU и память.
Подробное описание того, как эти параметры влияют на производительность и ресурсы, см. в этой статье блога. Как описано в этой статье, настройка может требовать тщательного баланса между этими двумя параметрами. Такое всестороннее тестирование часто непрактично, поэтому в целом мы рекомендуем:
• max_insert_threads: выберите ~ половину доступных ядер CPU для потоков вставки (чтобы оставить достаточно ядер для фоновых слияний)
• peak_memory_usage_in_bytes: выберите целевое пиковое потребление памяти: либо всю доступную оперативную память (если приём данных выполняется изолированно), либо половину или меньше (чтобы оставить ресурсы для других параллельных задач)
Затем:
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
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
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%. Предлагаем читателю самостоятельно проверить, удастся ли ещё больше повысить производительность на одном узле.
Масштабирование за счет ресурсов и узлов
Масштабирование за счет ресурсов и узлов применимо как к запросам на чтение, так и к запросам на вставку.
Вертикальное масштабирование
Во всех предыдущих примерах настройки и запросы выполнялись только на одном узле в нашем кластере ClickHouse Cloud. Однако во многих случаях вам будет доступно и несколько узлов ClickHouse. Мы рекомендуем сначала использовать вертикальное масштабирование, поскольку пропускная способность S3 растёт линейно с увеличением числа ядер. Если повторить наши предыдущие запросы на вставку и чтение на более мощном узле ClickHouse Cloud, увеличив ресурсы вдвое (64GiB, 16 vCPUs) и задав соответствующие настройки, оба запроса будут выполняться примерно в два раза быстрее.
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
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.)
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
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.)
Отдельные узлы также могут упираться в ограничения пропускной способности сети и запросов S3 GET, что не позволяет добиться линейного роста производительности при вертикальном масштабировании.
Горизонтальное масштабирование
Со временем горизонтальное масштабирование часто становится необходимым из-за ограничений по доступности оборудования и соображений экономической эффективности. В ClickHouse Cloud производственные кластеры включают как минимум 3 узла. Поэтому для вставки данных также может иметь смысл задействовать все узлы.
Использование кластера для чтения из S3 требует применения функции s3Cluster, как описано в разделе Использование кластеров. Это позволяет распределить чтение между узлами.
Сервер, который первым получает запрос на вставку, сначала разворачивает glob-шаблон, а затем динамически распределяет обработку каждого совпавшего файла между собой и другими серверами.
Мы повторяем предыдущий запрос на чтение, распределяя рабочую нагрузку между 3 узлами и изменяя запрос так, чтобы использовать s3Cluster. В ClickHouse Cloud это выполняется автоматически при обращении к кластеру default.
Как отмечено в разделе Использование кластеров, эта работа распределяется на уровне файлов. Чтобы воспользоваться этой возможностью, потребуется достаточное количество файлов, то есть как минимум больше, чем число узлов.
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
┌─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 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
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 будут выполняться на каждом сегменте — из/в базовую таблицу движка Distributed на каждом узле.
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
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.
Как и ожидалось, это снижает производительность вставки втрое.
Операции вставки иногда завершаются ошибками, например из-за тайм-аутов. Если вставка завершилась ошибкой, данные могли как записаться успешно, так и не записаться. Чтобы клиент мог безопасно повторить попытку вставки, по умолчанию в распределенных развертываниях, таких как ClickHouse Cloud, ClickHouse пытается определить, не были ли эти данные уже успешно вставлены. Если вставленные данные помечаются как дубликат, ClickHouse не записывает их в целевую таблицу. Однако пользователь все равно получит статус успешного выполнения операции — как если бы данные были вставлены обычным образом.
Хотя такое поведение, добавляющее накладные расходы на вставку, оправдано при загрузке данных с клиента или батчами, оно может быть излишним при выполнении INSERT INTO SELECT из объектного хранилища. Если отключить эту функциональность во время вставки, можно повысить производительность, как показано ниже:
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
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.
В ClickHouse параметр optimize_on_insert определяет, будут ли части данных объединяться в процессе вставки. Когда он включен (optimize_on_insert = 1 по умолчанию), небольшие части объединяются в более крупные по мере вставки, что повышает производительность запросов за счет сокращения числа частей, которые нужно читать. Однако такое слияние создает дополнительную нагрузку на процесс вставки и может замедлять вставки при высокой интенсивности записи.
Отключение этого параметра (optimize_on_insert = 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, optimize_on_insert = 0
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.)
- При нехватке памяти, если выполняете вставку в S3, можно уменьшить значение
max_insert_delayed_streams_for_parallel_write.
Последнее изменение 10 июня 2026 г.