Приложение
Postgres и ClickHouse: эквивалентные и различные концепции
Пользователи, приходящие из OLTP систем и привыкшие к транзакциям ACID, должны понимать, что ClickHouse делает сознательные компромиссы, не предоставляя их в полном объеме в обмен на производительность. Семантика ClickHouse может обеспечить высокие гарантии долговечности и высокую пропускную способность записи, если она хорошо понята. Мы выделяем некоторые ключевые концепции ниже, с которыми пользователи должны ознакомиться перед работой с ClickHouse из Postgres.
Шарды и реплики
Шардинг и репликация — это две стратегии, используемые для масштабирования за пределами одной экземпляра Postgres, когда хранилище и/или вычисления становятся узким местом для производительности. Шардинг в Postgres включает в себя разделение большой базы данных на меньшие, более управляемые части, распределенные по нескольким узлам. Тем не менее, Postgres не поддерживает шардирование нативно. Вместо этого шардирование может быть достигнуто с помощью расширений, таких как Citus, благодаря которым Postgres становится распределенной базой данных, способной масштабироваться горизонтально. Этот подход позволяет Postgres обрабатывать более высокие скорости транзакций и более крупные наборы данных, распределяя нагрузку между несколькими машинами. Шарды могут быть основаны на строках или схеме, чтобы обеспечить гибкость для типов нагрузок, таких как транзакционные или аналитические. Шардинг может ввести значительную сложность с точки зрения управления данными и выполнения запросов, так как требует координации между несколькими машинами и гарантии согласованности.
В отличие от шардов, реплики — это дополнительные экземпляры Postgres, которые содержат все или часть данных от основного узла. Реплики используются по разным причинам, включая повышенную производительность чтения и сценарии высокой доступности (HA). Физическая репликация является нативной функцией Postgres, которая включает в себя копирование всей базы данных или значительных ее частей на другой сервер, включая все базы данных, таблицы и индексы. Это включает в себя стриминг сегментов WAL с основного узла на реплики по TCP/IP. В отличие от этого, логическая репликация представляет собой более высокий уровень абстракции, который стримит изменения на основе операций INSERT
, UPDATE
и DELETE
. Хотя те же результаты могут применяться к физической репликации, она обеспечивает большую гибкость для таргетирования конкретных таблиц и операций, а также для преобразования данных и поддержки различных версий Postgres.
В отличие от этого, шардирование и репликация в ClickHouse — это две ключевые концепции, касающиеся распределения данных и избыточности. Реплики ClickHouse можно рассматривать как аналог реплик Postgres, хотя репликация в ClickHouse является в конечном итоге согласованной, без понятия основного узла. Шардинг, в отличие от Postgres, поддерживается нативно.
Шард — это часть данных вашей таблицы. У вас всегда есть как минимум один шард. Шардинг данных по нескольким серверам может использоваться для распределения нагрузки, если вы превышаете мощность одного сервера, при этом все шарды используются для выполнения запроса параллельно. Пользователи могут вручную создавать шарды для таблицы на разных серверах и напрямую вставлять в них данные. В качестве альтернативы можно использовать распределенную таблицу с ключом шардирования, определяющим, к какому шард данные направляются. Ключ шардирования может быть случайным или результатом хеш-функции. Важно отметить, что шард может состоять из нескольких реплик.
Реплика — это копия ваших данных. В ClickHouse всегда как минимум одна копия ваших данных, и минимальное количество реплик равно одному. Добавление второй реплики данных обеспечивает отказоустойчивость и потенциально дополнительные вычисления для обработки больших объемов запросов (Параллельные реплики также могут использоваться для распределения вычислений для одного запроса, тем самым снижая задержку). Реплики достигаются с помощью движка таблиц ReplicatedMergeTree, который позволяет ClickHouse поддерживать несколько копий данных в синхронизации между различными серверами. Репликация является физической: между узлами передаются только сжатые части, а не запросы.
В резюме, реплика — это копия данных, которая обеспечивает избыточность и надежность (и потенциально распределенную обработку), в то время как шард — это подмножество данных, которое позволяет выполнять распределенную обработку и балансировку нагрузки.
ClickHouse Cloud использует одну копию данных, хранящихся в S3 с несколькими вычислительными репликами. Данные доступны каждому узлу реплики, у каждого из которых есть локальный SSD-кэш. Это зависит от репликации метаданных только через ClickHouse Keeper.
Окончательная согласованность
ClickHouse использует ClickHouse Keeper (реализация ZooKeeper на C++, также можно использовать ZooKeeper) для управления своей внутренней механизмом репликации, сосредоточив внимание в первую очередь на хранении метаданных и обеспечении окончательной согласованности. Keeper используется для назначения уникальных последовательных номеров для каждой вставки в распределенной среде. Это критически важно для поддержания порядка и согласованности операций. Эта система также обрабатывает фоновые операции, такие как слияния и мутации, гарантируя, что работа по этим операциям распределена при обеспечении их выполнения в том же порядке для всех реплик. В дополнение к метаданным, Keeper функционирует как комплексный центр управления репликацией, включая отслеживание контрольных сумм для сохраненных частей данных, и действует как распределенная система уведомлений между репликами.
Процесс репликации в ClickHouse (1) начинается, когда данные вставляются в любую реплику. Эти данные в своем сыром формате вставки (2) записываются на диск вместе с их контрольными суммами. После записи реплика (3) пытается зарегистрировать эту новую часть данных в Keeper, выделяя уникальный номер блока и записывая детали новой части. Другие реплики, при (4) обнаружении новых записей в журнале репликации, (5) загружают соответствующую часть данных через внутренний протокол HTTP, проверяя ее по контрольным суммам, указанным в ZooKeeper. Этот метод гарантирует, что все реплики в конечном итоге будут содержать согласованные и актуальные данные, несмотря на различные скорости обработки или потенциальные задержки. Более того, система способна обрабатывать несколько операций одновременно, оптимизируя процессы управления данными и позволяя системе масштабироваться и оставаться устойчивой к аппаратным несоответствиям.

Обратите внимание, что ClickHouse Cloud использует оптимизированный для облака механизм репликации, адаптированный к его архитектуре разделения хранения и вычислений. Храня данные в общем объектном хранилище, данные автоматически становятся доступными для всех вычислительных узлов без необходимости физически реплицировать данные между узлами. Вместо этого Keeper используется только для обмена метаданными (где какие данные существуют в объектном хранилище) между вычислительными узлами.
PostgreSQL использует другую стратегию репликации по сравнению с ClickHouse, в первую очередь используя потоковую репликацию, которая включает в себя модель первичной реплики, где данные постоянно передаются от первичной к одной или нескольким репликам. Этот тип репликации обеспечивает близкую к реальному времени согласованность и является синхронным или асинхронным, предоставляя администраторам контроль над балансом между доступностью и согласованностью. В отличие от ClickHouse, PostgreSQL полагается на WAL (журнал предшествующих записей) с логической репликацией и декодированием для передачи объектов данных и изменений между узлами. Этот подход в PostgreSQL более прост, но может не обеспечить тот же уровень масштабируемости и отказоустойчивости в высоко распределенных средах, который достигает ClickHouse через сложное использование Keeper для координации распределенных операций и окончательной согласованности.
Последствия для пользователя
В ClickHouse возможность «грязных» чтений — когда пользователи могут записать данные в одну реплику, а затем прочитать потенциально непродуцированные данные из другой — возникает из его модели репликации с окончательной согласованностью, управляемой через Keeper. Эта модель акцентирует внимание на производительности и масштабируемости в распределенных системах, позволяя репликам работать независимо и синхронизироваться асинхронно. В результате новые вставленные данные могут не быть немедленно видимыми для всех реплик, в зависимости от задержки репликации и времени, необходимого для распространения изменений по системе.
С другой стороны, модель потоковой репликации PostgreSQL обычно может предотвращать грязные чтения, используя опции синхронной репликации, когда первичная реплика ждет, пока по меньшей мере одна реплика подтвердит получение данных перед подтверждением транзакций. Это гарантирует, что после подтверждения транзакции существует гарантия того, что данные доступны в другой реплике. В случае сбоя первичной реплики, реплика обеспечит, чтобы запросы видели подтвержденные данные, тем самым поддерживая более строгий уровень согласованности.
Рекомендации
Пользователи, впервые работающие с ClickHouse, должны осознавать эти различия, которые проявятся в реплицированных средах. Обычно окончательная согласованность является достаточной в аналитике по миллиардам, если не триллионам, точек данных, где метрики либо более стабильны, либо оценка является достаточной, поскольку новые данные постоянно вставляются с высокой скоростью.
Существуют несколько вариантов для повышения согласованности чтений, если это необходимо. Оба примера требуют либо увеличенной сложности, либо накладных расходов — снижая производительность запросов и усложняя масштабирование ClickHouse. Мы рекомендуем эти подходы только в случае крайней необходимости.
Согласованная маршрутизация
Чтобы преодолеть некоторые ограничения окончательной согласованности, пользователи могут обеспечить маршрутизацию клиентов к одним и тем же репликам. Это полезно в случаях, когда несколько пользователей запрашивают ClickHouse и результаты должны быть детерминированы по запросам. Хотя результаты могут различаться, когда новые данные вставляются, те же реплики должны запрашиваться, обеспечивая согласованный обзор.
Это можно достичь несколькими способами в зависимости от вашей архитектуры и используете ли вы ClickHouse OSS или ClickHouse Cloud.
ClickHouse Cloud
ClickHouse Cloud использует одну копию данных, хранящихся в S3 с несколькими вычислительными репликами. Данные доступны каждому узлу реплики, у каждого из которых есть локальный SSD-кэш. Чтобы обеспечить согласованные результаты, пользователям необходимо только обеспечить согласованную маршрутизацию на один и тот же узел.
Связь с узлами службы ClickHouse Cloud осуществляется через прокси. Подключения по протоколу HTTP и Native будут направлены на один и тот же узел на период, в течение которого они остаются открытыми. В случае соединений HTTP 1.1 от большинства клиентов это зависит от окна Keep-Alive. Это можно настроить на большинстве клиентов, например, Node Js. Это также требует конфигурации на стороне сервера, которая будет выше, чем у клиента, и установлена на 10 секунд в ClickHouse Cloud.
Чтобы обеспечить согласованную маршрутизацию по соединениям, например, если используется пул соединений или если соединения истекают, пользователи могут либо обеспечить использование одного и того же соединения (легче для native), либо запросить выставление стабильных конечных точек. Это предоставляет набор конечных точек для каждого узла в кластере, что позволяет клиентам гарантировать, что запросы направляются детерминированно.
Обратитесь в службу поддержки для получения доступа к стабильным конечным точкам.
ClickHouse OSS
Для достижения этого поведения в OSS зависит от вашей топологии шардов и реплик и используете ли вы распределенную таблицу для запросов.
Когда у вас только один шард и реплики (что обычно, поскольку ClickHouse вертикально масштабируется), пользователи выбирают узел на уровне клиента и запрашивают реплику напрямую, обеспечивая, что она выбирается детерминированно.
Хотя топологии с несколькими шарами и репликами возможны без распределенной таблицы, такие сложные развертывания обычно имеют свою собственную инфраструктуру маршрутизации. Поэтому мы предполагаем, что развертывания с более чем одним шардом используют распределенную таблицу (распределенные таблицы могут использоваться с развертываниями с одним шардом, но обычно не являются необходимыми).
В этом случае пользователи должны обеспечить выполнение согласованной маршрутизации узлов на основе свойства, например, session_id
или user_id
. Настройки prefer_localhost_replica=0
, load_balancing=in_order
должны быть установлены в запросе. Это обеспечит предпочтение к любым локальным репликам шардов, с репликами, предпочтительными в соответствии с конфигурацией в противном случае — при условии, что у них одинаковое количество ошибок — произойдет автоматический переход на случайный выбор, если ошибок больше. load_balancing=nearest_hostname
также может использоваться как альтернатива для этого детерминированного выбора шара.
При создании распределенной таблицы пользователи укажут кластер. Это определение кластера, указанное в config.xml, перечислит шары (и их реплики) — тем самым позволяя пользователям управлять порядком их использования с каждого узла. Используя это, пользователи могут гарантировать, что выбор детерминированный.
Последовательная согласованность
В исключительных случаях пользователям может понадобиться последовательная согласованность.
Последовательная согласованность в базах данных — это когда операции над базой данных выглядят как выполненные в некотором последовательном порядке, и этот порядок согласован между всеми процессами, взаимодействующими с базой данных. Это означает, что каждая операция, кажется, влияет мгновенно между ее вызовом и завершением, и есть единственный согласованный порядок, в котором все операции наблюдаются любым процессом.
С точки зрения пользователя это обычно проявляется как необходимость записывать данные в ClickHouse и при чтении данных гарантировать, что возвращаются новейшие вставленные строки. Это можно достичь несколькими способами (в порядке предпочтения):
- Чтение/запись с одного узла — Если вы используете нативный протокол или сессию для вашей записи/чтения через HTTP, вы должны быть подключены к одной и той же реплике: в этом сценарии вы читаете напрямую с узла, на который записываете, тогда ваше чтение всегда будет согласованным.
- Синхронизация реплик вручную — Если вы записываете в одну реплику и читаете из другой, вы можете использовать команду
SYSTEM SYNC REPLICA LIGHTWEIGHT
перед чтением. - Включение последовательной согласованности — через настройку запроса
select_sequential_consistency = 1
. В OSS также должно быть указано значение настройкиinsert_quorum = 'auto'
.
Смотрите здесь для получения дальнейших деталей о включении этих настроек.
Использование последовательной согласованности будет создавать большую нагрузку на ClickHouse Keeper. Это может значить более медленные вставки и чтения. SharedMergeTree, используемый в ClickHouse Cloud в качестве основного движка таблицы, последовательная согласованность несет меньшие накладные расходы и будет лучше масштабироваться. Пользователи OSS должны использовать этот подход с осторожностью и измерять нагрузку на Keeper.
Поддержка транзакций (ACID)
Пользователи, мигрирующие из PostgreSQL, могут быть привыкли к его надежной поддержке свойств ACID (Атомарность, Согласованность, Изолированность, Долговечность), что делает его надежным выбором для транзакционных баз данных. Атомарность в PostgreSQL гарантирует, что каждая транзакция рассматривается как единое целое, которое либо полностью завершается, либо полностью откатывается, предотвращая частичные обновления. Согласованность поддерживается за счет применения ограничений, триггеров и правил, которые гарантируют, что все транзакции базы данных приводят к действительному состоянию. Уровни изолированности, от Read Committed до Serializable, поддерживаются в PostgreSQL, позволяя тонко контролировать видимость изменений, сделанных одновременно выполняемыми транзакциями. Наконец, Долговечность достигается с помощью журнала предварительных записей (WAL), который обеспечивает, что после подтверждения транзакции она остается таковой даже в случае сбоя системы.
Эти свойства распространены для OLTP баз данных, которые служат источником правды.
Хотя это мощно, это имеет свои ограничения и делает PB-габариты сложными. ClickHouse идет на компромисс с этими свойствами, чтобы обеспечить быстрые аналитические запросы в масштабе при поддержании высокой пропускной способности записи.
ClickHouse предоставляет свойства ACID при ограниченной конфигурации — проще всего при использовании нереплицированного экземпляра движка таблиц MergeTree с одной партицией. Пользователи не должны ожидать этих свойств вне этих случаев и должны удостовериться, что они не являются необходимыми.
Сжатие
Колонко-ориентированное хранилище ClickHouse означает, что сжатие, как правило, будет значительно лучше по сравнению с Postgres. Следующее иллюстрирует сравнение требований к хранению для всех таблиц Stack Overflow в обеих базах данных:
Дополнительные сведения об оптимизации и измерении сжатия можно найти здесь.
Соответствие типов данных
Следующая таблица показывает эквивалентные типы данных ClickHouse для Postgres.
Тип данных Postgres | Тип ClickHouse |
---|---|
DATE | Date |
TIMESTAMP | DateTime |
REAL | Float32 |
DOUBLE | Float64 |
DECIMAL, NUMERIC | Decimal |
SMALLINT | Int16 |
INTEGER | Int32 |
BIGINT | Int64 |
SERIAL | UInt32 |
BIGSERIAL | UInt64 |
TEXT, CHAR, BPCHAR | String |
INTEGER | Nullable(Int32) |
ARRAY | Array |
FLOAT4 | Float32 |
BOOLEAN | Bool |
VARCHAR | String |
BIT | String |
BIT VARYING | String |
BYTEA | String |
NUMERIC | Decimal |
GEOGRAPHY | Point, Ring, Polygon, MultiPolygon |
GEOMETRY | Point, Ring, Polygon, MultiPolygon |
INET | IPv4, IPv6 |
MACADDR | String |
CIDR | String |
HSTORE | Map(K, V), Map(K,Variant) |
UUID | UUID |
ARRAY<T> | ARRAY(T) |
JSON* | String, Variant, Nested, Tuple |
JSONB | String |
* Поддержка JSON в ClickHouse на производстве находится в разработке. В настоящее время пользователи могут либо отображать JSON как строку и использовать функции JSON, либо отображать JSON напрямую на кортежи и вложенные, если структура предсказуема. Прочитайте подробнее о JSON здесь.