Описанный в этом руководстве подход можно применять и к другим реализациям объектного хранилища с собственными специализированными табличными функциями, таким как GCS и Azure Blob storage.
Механика вставки (один узел)
Размер блока вставки
INSERT INTO SELECT ClickHouse получает некоторую порцию данных и ① формирует из неё (как минимум) один блок вставки в памяти (для каждого ключа партиционирования). Данные в блоке сортируются, и к ним применяются оптимизации, специфичные для движка таблицы. Затем данные сжимаются и ② записываются в хранилище базы данных в виде новой части данных.
Размер блока вставки влияет как на дисковый файловый ввод-вывод, так и на потребление памяти сервером ClickHouse. Более крупные блоки вставки требуют больше памяти, но создают более крупные и менее многочисленные исходные части. Чем меньше частей ClickHouse нужно создать для загрузки большого объёма данных, тем меньше требуется дискового ввода-вывода и автоматических фоновых слияний.
При использовании запроса INSERT INTO SELECT в сочетании с движком интеграционной таблицы или табличной функцией данные загружаются сервером ClickHouse:
Пока данные не будут полностью загружены, сервер выполняет цикл:
min_insert_block_size_rows(по умолчанию:1048545строк)min_insert_block_size_bytes(по умолчанию:256 MiB)
min_insert_block_size_bytes обозначает размер несжатого блока в памяти (а не размер сжатой части на диске). Также обратите внимание, что созданные блоки и части редко содержат в точности заданное количество строк или байтов, поскольку ClickHouse обрабатывает данные в потоковом режиме, построчно-блоками. Поэтому эти настройки задают минимальные пороговые значения.
Учитывайте слияния
MergeTree).
Параллелизм вставки
max_insert_threads. Значение по умолчанию — 1 для open-source ClickHouse и 4 для ClickHouse Cloud.
При большом количестве файлов параллельная обработка несколькими потоками вставки работает эффективно. Она может полностью загрузить как доступные ядра CPU, так и пропускную способность сети (при параллельной загрузке файлов). В сценариях, когда в таблицу загружается всего несколько крупных файлов, ClickHouse автоматически обеспечивает высокий уровень параллелизма обработки данных и оптимизирует использование пропускной способности сети, создавая дополнительные потоки чтения для каждого потока вставки, чтобы параллельно читать (загружать) разные диапазоны внутри крупных файлов.
Для функции и таблицы s3 параллельная загрузка отдельного файла определяется значениями max_download_threads и 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 для повышения пропускной способности автоматически выполняет предзагрузку данных, асинхронно читая такие файлы заранее.
Измерение производительности
Влияние конфигурации оборудования
- поддерживаемый начальный размер частей
- возможный уровень параллелизма вставки
- производительность фоновых слияний частей
Один и тот же регион
Форматы
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 доступен по всему миру и обычно обеспечивает высокую пропускную способность.
Пример набора данных
SELECT, которые возвращают клиенту большие объёмы данных, используйте для запросов либо формат Null, либо направляйте результаты в движок Null. Это поможет избежать перегрузки клиента данными и насыщения сети.
При выполнении запросов на чтение первый запрос часто может казаться медленнее, чем повторное выполнение того же запроса. Это может быть связано как с собственным кэшированием S3, так и с кэшем вывода схемы ClickHouse. В нём сохраняется выведенная схема файлов, благодаря чему при последующих обращениях можно пропустить этап вывода схемы и тем самым сократить время выполнения запроса.
Использование потоков для чтения
- Обычно значения
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равно 10MiB. В некоторых случаях можно безопасно увеличить размер этого буфера до 50 MB (max_download_buffer_size=52428800), чтобы небольшие файлы загружались только одним потоком. Это может сократить время, которое каждый поток тратит на вызовы S3, и тем самым уменьшить время ожидания S3. Пример см. в этой записи блога.
system.query_log.
Рассмотрим наш предыдущий запрос: удвоение max_threads до 16 (значение max_thread по умолчанию равно числу ядер на узле) повышает производительность запроса на чтение в 2 раза ценой большего потребления памяти. Дальнейшее увеличение max_threads дает все меньший эффект, как показано.
Настройка потоков и размера блоков для вставки
- Чем больше размер блока вставки, тем меньше частей ClickHouse приходится создавать и тем меньше требуется дисковых операций ввода-вывода и фоновых слияний.
- Чем выше число параллельных потоков вставки, тем быстрее будут обрабатываться данные.
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
33%. Предлагаем читателю проверить, удастся ли ему ещё больше повысить производительность на одном узле.
Масштабирование за счёт ресурсов и узлов
Вертикальное масштабирование
Производительность отдельных узлов также может упираться в ограничения сети и S3 GET-запросов, что не позволяет добиваться линного прироста производительности при вертикальном масштабировании.
Горизонтальное масштабирование
s3Cluster, как описано в разделе Использование кластеров. Это позволяет распределять чтение между узлами.
Сервер, который первым получает запрос на вставку, сначала разворачивает glob-шаблон, а затем динамически распределяет обработку каждого подходящего файла между собой и другими серверами.
Повторим предыдущий запрос на чтение, распределив нагрузку между 3 узлами и изменив запрос так, чтобы использовать s3Cluster. В ClickHouse Cloud это делается автоматически — достаточно указать кластер default.
Как отмечено в разделе Использование кластеров, эта работа распределяется на уровне файлов. Чтобы воспользоваться этой возможностью, потребуется достаточное количество файлов, то есть как минимум больше, чем число узлов.
s3Cluster, вставка выполняется через узел-инициатор. Это означает, что, хотя чтение происходит на каждом узле, полученные строки для распределения направляются на узел-инициатор. В сценариях с высокой пропускной способностью это может стать узким местом. Чтобы решить эту проблему, задайте параметр parallel_distributed_insert_select для функции s3cluster.
Значение parallel_distributed_insert_select=2 гарантирует, что SELECT и INSERT будут выполняться на каждом шарде в базовую таблицу распределённого движка и из неё на каждом узле.
Дополнительная настройка
Отключение дедупликации
INSERT INTO SELECT из объектного хранилища. Если отключить эту функциональность во время вставки, можно повысить производительность, как показано ниже:
Оптимизация при вставке
optimize_on_insert определяет, будут ли части данных сливаться в процессе вставки. Когда она включена (optimize_on_insert = 1 по умолчанию), небольшие части сливаются в более крупные по мере вставки, что повышает производительность запросов за счёт сокращения числа частей, которые нужно читать. Однако такое слияние создаёт дополнительную нагрузку на процесс вставки и может замедлить вставки с высокой пропускной способностью.
Отключение этой настройки (optimize_on_insert = 0) отключает слияние во время вставки, позволяя записывать данные быстрее, особенно при частых небольших вставках. Процесс слияния переносится в фоновый режим, что улучшает производительность вставки, но временно увеличивает количество небольших частей, из-за чего запросы могут выполняться медленнее, пока фоновое слияние не завершится. Эта настройка особенно полезна, когда в приоритете скорость вставки, а фоновый процесс слияния может позже эффективно выполнить оптимизацию. Как показано ниже, отключение этой настройки может повысить пропускную способность вставки:
Прочие замечания
- При нехватке памяти, если вы вставляете данные в S3, попробуйте уменьшить
max_insert_delayed_streams_for_parallel_write.