@clickhouse/client- только для Node.js@clickhouse/client-web- браузеры (Chrome/Firefox), Cloudflare workers
Навыки AI agentJS-клиент включает навыки AI agent, которые помогают агентам для написания кода работать с клиентом. Установите их командой:
Требования к окружению (node.js)
Требования к окружению (веб)
Установка
Совместимость с ClickHouse
Скорее всего, клиент будет работать и с более ранними версиями, однако такая поддержка предоставляется по мере возможности и не гарантируется. Если вы используете версию ClickHouse старее 23.3, ознакомьтесь с политикой безопасности ClickHouse и рассмотрите возможность обновления.
Примеры
API клиента
Создание экземпляра клиента
createClient:
Конфигурация
Параметры конфигурации для Node.js
Настройка URL
http[s]://[username:password@]hostname:port[/database][?param1=value1¶m2=value2]. Почти во всех случаях имя конкретного параметра соответствует его пути в интерфейсе параметров конфигурации, за несколькими исключениями. Поддерживаются следующие параметры:
- (1) Для логических значений допустимы
true/1иfalse/0. - (2) У любого параметра с префиксом
clickhouse_setting_илиch_этот префикс будет удален, а оставшаяся часть добавлена вclickhouse_settingsклиента. Например,?ch_async_insert=1&ch_wait_for_async_insert=1эквивалентно:
clickhouse_settings следует передавать в URL как 1/0.
- (3) Аналогично (2), но для конфигурации
http_header. Например,?http_header_x-clickhouse-auth=foobarбудет эквивалентен:
Подключение
Подготовьте сведения о подключении
Сведения о подключении для вашего сервиса ClickHouse Cloud доступны в консоли ClickHouse Cloud.
Выберите сервис и нажмите Connect:
Выберите HTTPS. Сведения о подключении будут показаны в примере команды
curl.
Если вы используете самоуправляемый ClickHouse, сведения о подключении задаёт ваш администратор ClickHouse.
Обзор подключения
url (включая
протокол и порт) и password заданы через переменные окружения, а также используется пользователь default.
Пример: создание экземпляра клиента Node.js с использованием переменных окружения для настройки.
Пул соединений (только Node.js)
10, но его можно изменить с помощью параметра конфигурации max_open_connections configuration option.
Нет гарантии, что для последующих запросов будет использоваться одно и то же соединение из пула, если только пользователь не укажет max_open_connections: 1. Это требуется редко, но может быть необходимо, если используются временные таблицы.
См. также: настройка Keep-Alive.
Query ID
command, exec, insert, select), возвращает query_id в результате. Этот уникальный идентификатор назначается клиентом для каждого запроса и может быть полезен для получения данных из system.query_log,
если он включен в конфигурации сервера, или для отмены длительно выполняющихся запросов (см. пример). При необходимости пользователь может переопределить query_id в параметрах методов command/query/exec/insert.
Общий параметр для всех клиентских методов
Метод query
SELECT, а также для отправки DDL-запросов, таких как CREATE TABLE, поэтому его нужно вызывать с await. Предполагается, что возвращённый результирующий набор будет обрабатываться в приложении.
Абстракции результирующего набора и строки
ResultSet предоставляет несколько удобных методов для обработки данных в приложении.
Реализация ResultSet для Node.js использует Stream.Readable, а веб-версия — Web API ReadableStream.
Вы можете прочитать ResultSet, вызвав методы text или json, и загрузить в память весь набор строк, возвращённый запросом.
Начинать чтение ResultSet следует как можно раньше, поскольку он удерживает поток ответа открытым и, как следствие, оставляет занятым базовое соединение. Клиент не буферизует входящие данные, чтобы избежать потенциально чрезмерного использования памяти приложением.
Если же набор слишком велик, чтобы целиком поместиться в памяти, можно вызвать метод stream и обрабатывать данные в режиме стриминга. В этом случае каждый фрагмент ответа будет преобразован в относительно небольшой массив строк (размер массива зависит от размера конкретного фрагмента, полученного клиентом от сервера, который может различаться, а также от размера отдельной строки), по одному фрагменту за раз.
Обратитесь к списку поддерживаемых форматов данных, чтобы определить, какой формат лучше всего подходит для стриминга в вашем случае. Например, если вы хотите передавать в потоке объекты JSON, можно выбрать JSONEachRow, и тогда каждая строка будет разобрана как объект JS, либо использовать более компактный формат JSONCompactColumns, при котором каждая строка будет представлена компактным массивом значений. См. также: потоковая передача файлов.
JSONEachRow, который считывает весь поток и разбирает содержимое в объекты JS.
Исходный код.
JSONEachRow с использованием классического подхода on('data'). Этот вариант взаимозаменяем с синтаксисом for await const. Исходный код.
CSV с использованием классического подхода on('data'). Этот вариант взаимозаменяем с синтаксисом for await const.
Исходный код
JSONEachRow, обрабатываемый с помощью синтаксиса for await const. Этот способ взаимозаменяем с классическим подходом on('data').
Исходный код.
Синтаксис
for await const требует немного меньше кода, чем подход с on('data'), но может негативно сказаться на производительности.
Подробнее см. в этом issue в репозитории Node.js.ReadableStream объектов.
Метод INSERT
{ query_id: '...', executed: false }. Если в этом случае query_id не был передан в параметрах метода, в результате это будет пустая строка, поскольку возврат случайного UUID, сгенерированного клиентом, мог бы ввести в заблуждение: запрос с таким query_id не будет существовать в таблице system.query_log.
Если оператор вставки был отправлен на сервер, флаг executed будет иметь значение true.
Метод INSERT и стриминг в Node.js
Stream.Readable, либо с обычным Array<T> — в зависимости от формата данных, указанного для метода insert. См. также раздел о потоковой передаче файлов.
Метод INSERT следует вызывать с await; однако можно передать входной поток и дождаться завершения операции insert позже — только после того, как поток будет завершен (в этот момент также будет разрешен промис insert). Это может быть полезно для обработчиков событий и подобных сценариев, но обработка ошибок на стороне клиента здесь может быть нетривиальной из-за множества пограничных случаев. Вместо этого рекомендуется использовать асинхронные вставки, как показано в этом примере.
Ограничения веб-версии
@clickhouse/client-web поддерживают только форматы Array<T> и JSON*.
Вставка потоков в веб-версии пока не поддерживается из-за ограниченной совместимости браузеров.
Поэтому интерфейс InsertParams для веб-версии немного отличается от версии для Node.js,
поскольку values могут иметь только тип ReadonlyArray<T>:
Метод Command
CREATE TABLE или ALTER TABLE.
Его следует вызывать с await.
Поток ответа немедленно закрывается, а базовый сокет освобождается.
Метод Exec
query/insert,
и вам нужен результат, можно использовать exec как альтернативу command.
exec возвращает поток для чтения, который ОБЯЗАТЕЛЬНО нужно либо прочитать, либо уничтожить на стороне приложения.
Ping
ping, предназначенный для проверки состояния подключения, возвращает true, если сервер доступен.
Если сервер недоступен, в результат также включается исходная ошибка.
/ping, а веб-версия — простой запрос SELECT 1, чтобы добиться аналогичного результата, поскольку конечная точка /ping не поддерживает CORS.
Пример: (Node.js/Web) Простой ping инстанса сервера ClickHouse. Примечание: в веб-версии перехваченные ошибки будут отличаться.
Исходный код.
ping также проверять учетные данные или указывать дополнительные параметры, такие как query_id, это можно сделать следующим образом:
query — см. определение типа PingParamsWithSelectQuery.
Close (только Node.js)
Потоковая передача файлов (только Node.js)
- Потоковая передача из NDJSON‑файла
- Потоковая передача из CSV-файла
- Потоковая передача из файла Parquet
- Потоковая запись в файл Parquet
query (JSONEachRow, CSV и т. д.), и имя выходного файла.
Поддерживаемые форматы данных
format как один из форматов семейства JSON (JSONEachRow, JSONCompactEachRow и т. д.), клиент будет сериализовать и десериализовать данные при передаче по сети.
Данные в “сырых” текстовых форматах (семейства CSV, TabSeparated и CustomSeparated) передаются по сети без дополнительных преобразований.
Для Parquet основной сценарий использования
select, вероятно, — запись результирующего потока в файл. См. пример в репозитории клиента.
JSONEachRowWithProgress — это формат только для вывода, поддерживающий передачу информации о Прогрессе в потоке. Подробнее см. в этом примере.
Полный список форматов ввода и вывода ClickHouse доступен
здесь.
Поддерживаемые типы данных ClickHouse
Соответствующий тип JS применим ко всем форматам
JSON*, кроме тех, которые представляют всё в виде строки (например, JSONStringEachRow)
Полный список поддерживаемых форматов ClickHouse доступен
здесь.
См. также:
Ограничения типов Date/Date32
Date/Date32 можно вставлять только
строки.
Пример: Вставьте значение типа Date.
Исходный код
DateTime или DateTime64, можно передавать как строки, так и объекты JS Date. Объекты JS Date можно передавать в insert как есть, если для date_time_input_format задано значение best_effort. Подробнее см. в этом примере.
Особенности типов Decimal*
JSON*. Предположим, у нас есть таблица, определённая следующим образом:
JSON* ClickHouse по умолчанию возвращает значения Decimal как числа, что может привести к потере точности. Чтобы этого избежать, в запросе можно привести значения Decimal к строке:
Целочисленные типы: Int64, Int128, Int256, UInt64, UInt128, UInt256
JSON* они возвращаются как строки, чтобы избежать
целочисленного переполнения, поскольку максимальные значения этих типов превышают Number.MAX_SAFE_INTEGER.
Однако это поведение можно изменить
с помощью настройки output_format_json_quote_64bit_integers
.
Пример: Настройте формат вывода JSON для 64-битных чисел.
Настройки ClickHouse
Дополнительные темы
Запросы с параметрами
name— идентификатор заполнителя.data_type- тип данных значения параметра приложения.
Сжатие
GZIP.
response: trueуказывает ClickHouse server возвращать сжатое тело ответа. Значение по умолчанию:response: falserequest: trueвключает сжатие тела запроса клиента. Значение по умолчанию:request: false
Логирование (только для Node.js)
stdout через методы console.debug/info, а в stderr — через методы console.warn/error.
Вы можете настроить логику логирования, указав LoggerClass, и выбрать нужный уровень логирования с помощью параметра level (по умолчанию — WARN):
TRACE- низкоуровневая информация о жизненном цикле сокетов Keep-AliveDEBUG- информация об ответе (без заголовков авторизации и сведений о хосте)INFO- в основном не используется; при инициализации клиента выводит текущий уровень логированияWARN- нефатальные ошибки; неудачный запросpingзаписывается как предупреждение, поскольку исходная ошибка включается в возвращаемый результатERROR- фатальные ошибки в методахquery/insert/exec/command, например при сбое запроса
Сертификаты TLS (только для Node.js)
certs,
а имя файла CA — CA.pem:
Конфигурация Keep-Alive (только для Node.js)
Connection: keep-alive будет отправляться автоматически. Бездействующие сокеты по умолчанию остаются в пуле соединений в течение 2500 миллисекунд (см. примечания по настройке этой опции).
Значение keep_alive.idle_socket_ttl должно быть заметно ниже, чем в конфигурации сервера/LB. Основная причина в том, что HTTP/1.1 позволяет серверу закрывать сокеты без уведомления клиента, поэтому, если сервер или балансировщик нагрузки закроет соединение раньше клиента, клиент может попытаться повторно использовать уже закрытый сокет, что приведёт к ошибке socket hang up.
Если вы изменяете keep_alive.idle_socket_ttl, имейте в виду, что его значение всегда должно быть согласовано с конфигурацией Keep-Alive на сервере/LB и всегда быть ниже неё, чтобы сервер никогда не закрывал открытое соединение первым.
Настройка idle_socket_ttl
keep_alive.idle_socket_ttl равным 2500 миллисекундам, поскольку это считается наиболее безопасным значением по умолчанию; на стороне сервера keep_alive_timeout может быть установлен вплоть до 3 секунд в версиях ClickHouse до 23.11 без изменений в config.xml.
Узнать правильное значение тайм-аута Keep-Alive можно в заголовках ответа сервера, выполнив следующую команду:
Connection и Keep-Alive в ответе. Например:
keep_alive_timeout составляет 10 секунд, и можно попробовать увеличить keep_alive.idle_socket_ttl до 9000 или даже 9500 миллисекунд, чтобы бездействующие сокеты оставались открытыми немного дольше, чем по умолчанию. Следите за возможными ошибками “Socket hang-up” — они указывают на то, что сервер закрывает соединения раньше, чем клиент; уменьшайте значение, пока ошибки не исчезнут.
Устранение неполадок
socket hang up даже при использовании последней версии клиента, проблему можно попытаться решить следующими способами:
-
Включите логирование как минимум с уровнем
WARN(по умолчанию). Это позволит проверить, нет ли в прикладном коде непрочитанного или зависшего потока: транспортный уровень запишет это в лог на уровне WARN, так как в противном случае сервер может закрыть сокет. Включить логирование в конфигурации клиента можно так: -
Убедитесь, что нужная конфигурация применяется к правильному экземпляру клиента. Если в приложении используется несколько экземпляров клиента, перепроверьте, что у того, который вы используете для запросов, задано корректное значение
keep_alive.idle_socket_ttl. -
Уменьшите значение
keep_alive.idle_socket_ttlв конфигурации клиента на 500 миллисекунд. В некоторых ситуациях, например при высокой сетевой задержке между клиентом и сервером, это может помочь исключить сценарий, при котором исходящий запрос получает сокет, который сервер уже собирается закрыть. -
Если эта ошибка возникает во время длительно выполняющихся запросов без входящих или исходящих данных (например, при длительном
INSERT FROM SELECT), причиной может быть балансировщик нагрузки или другие сетевые компоненты, закрывающие долгоживущие соединения или запросы, которые выполняются слишком долго. В таком случае можно попробовать принудительно отправлять некоторые данные во время выполнения длительных запросов, используя комбинацию следующих настроек ClickHouse:Однако имейте в виду, что в последних версиях Node.js общий размер полученных заголовков ограничен 16 КБ; после получения определенного количества заголовков прогресса — в наших тестах это было около 70–80 — будет сгенерировано исключение. Также можно использовать совершенно другой подход, полностью исключив ожидание передачи данных по сети; для этого можно воспользоваться особенностью HTTP interface, заключающейся в том, что мутации не отменяются при потере соединения. Подробнее см. в этом примере (часть 2). -
Возможность Keep-Alive можно полностью отключить. В этом случае клиент также будет добавлять заголовок
Connection: closeк каждому запросу, а базовый HTTP-агент не будет повторно использовать соединения. Настройкаkeep_alive.idle_socket_ttlбудет игнорироваться, так как бездействующих сокетов не будет. Это приведет к дополнительным накладным расходам, поскольку для каждого запроса будет устанавливаться новое соединение. -
Исключите возможные проблемы в остальной части сетевого стека, включая сам Node.js, выполнив простой тест из командной строки с тем же экземпляром ClickHouse и по тому же сетевому пути (то есть с той же машины или из того же сегмента сети, например из Kubernetes пода), например с помощью
curl:Возможно, стоит запустить его в цикле на несколько минут. Если вы видите похожие ошибки вcurl, скорее всего, проблема связана не с конфигурацией клиента, а с сетевым стеком или конфигурацией сервера. -
Чтобы проверить соединение средствами самого Node.js, можно попробовать создать простой HTTP-запрос к серверу ClickHouse, используя встроенный API
fetch:
-
В некоторых случаях прикладной код или адаптеры фреймворка могут добавлять упреждающий
ping()перед фактическим выполнением запроса. В результатеping()может завершиться успешно, а следующий запрос — завершиться ошибкой “socket hang up” из-за той же проблемы с бездействующими соединениями. Если вы видите такую картину в журнале, проверьте, можно ли отключить упреждающиеping()в используемом фреймворке или прикладном коде. Это также должно снизить вероятность того, что промежуточные сетевые компоненты начнут ограничивать трафик. - Убедитесь, что самому приложению выделяется достаточно процессорного времени и что сеть не ограничивается хостинг-провайдером. Различные средства мониторинга, такие как метрики пауз GC, метрики задержки цикла событий и другие подобные показатели, тоже могут помочь исключить возможные проблемы, связанные с нехваткой ресурсов.
- Попробуйте проверять свой прикладной код с включенным правилом ESLint no-floating-promises — оно поможет выявить необработанные промисы, которые могут приводить к зависающим потокам и сокетам.
Пользователи в режиме «только для чтения»
readonly=1 сжатие ответа включить нельзя, так как для этого требуется настройка enable_http_compression. Следующая конфигурация приведет к ошибке:
readonly=1.
Прокси с путём в URL
http://proxy:8123/clickhouse_server, укажите clickhouse_server в параметре конфигурации pathname (с начальным слешем или без него); в противном случае, если указать его напрямую в url, он будет интерпретирован как параметр database. Поддерживается несколько сегментов, например /my_proxy/db.
Обратный прокси с аутентификацией
http_headers, чтобы передать необходимые заголовки:
Пользовательский HTTP/HTTPS-агент (экспериментальный, только для Node.js)
max_open_connections, keep_alive.enabled, tls), и именно он будет управлять подключениями к серверу ClickHouse. Кроме того, если используются TLS-сертификаты, базовый агент будет настроен с необходимыми сертификатами, а также будут применяться корректные заголовки TLS-аутентификации.
Начиная с версии 1.2.0, клиенту можно передать пользовательский HTTP- или HTTPS-агент, заменив стандартный базовый агент. Это может быть полезно в случае сложных сетевых конфигураций. Если передан пользовательский агент, действуют следующие условия:
- Параметры
max_open_connectionsиtlsне будут иметь эффекта и будут игнорироваться клиентом, так как относятся к конфигурации базового агента. keep_alive.enabledбудет регулировать только значение по умолчанию заголовкаConnection(true->Connection: keep-alive,false->Connection: close).- Хотя управление бездействующими keep-alive-сокетами по-прежнему будет работать (поскольку оно привязано не к агенту, а к конкретному сокету), теперь его можно полностью отключить, установив значение
keep_alive.idle_socket_ttlв0.
Примеры использования собственного агента
set_basic_auth_header (Добавленный в 1.2.0), так как он конфликтует с заголовками TLS. Все заголовки TLS нужно указать вручную.
Известные ограничения (Node.js/web)
- Для результирующих наборов нет мапперов данных, поэтому используются только примитивы языка. Поддержка некоторых мапперов типов данных запланирована в рамках поддержки формата RowBinary.
- Есть некоторые особенности типов данных Decimal* и Date* / DateTime*.
- При использовании форматов семейства JSON* числа, превышающие Int32, представлены в виде строк, поскольку максимальные значения типов Int64+ больше
Number.MAX_SAFE_INTEGER. Подробнее см. в разделе Целочисленные типы.
Известные ограничения (web)
- Стриминг
select-запросов работает, но для вставок отключён (в том числе на уровне типов). - Сжатие запросов отключено, а его настройка игнорируется. Сжатие ответов работает.
- Поддержка логирования пока отсутствует.
Советы по оптимизации производительности
- Чтобы снизить потребление памяти приложением, при работе с большими вставками (например, из файлов) и выборками там, где это уместно, стоит использовать потоки. Для обработчиков событий и похожих сценариев хорошим вариантом также могут быть async inserts: они позволяют свести к минимуму или даже полностью избежать батчинга на стороне клиента. Примеры async insert доступны в client repository; префикс имени файла —
async_insert_. - По умолчанию клиент не включает сжатие запросов и ответов. Однако при выборке или вставке больших объёмов данных можно рассмотреть его включение через
ClickHouseClientConfigOptions.compression(либо только дляrequestилиresponse, либо для обоих). - Сжатие даёт заметный штраф по производительности. Включение сжатия для
requestилиresponseотрицательно скажется соответственно на скорости выборок или вставок, но уменьшит объём сетевого трафика, передаваемого приложением.
Свяжитесь с нами
#clickhouse-js) или через GitHub issues.