Найти тему
Krestoform

Обновление данных в таблицах ClickHouse

Немножко о ClickHouse, который не тормозит, о том как мы работаем с ClickHouse, и чутка об SRE и чутка о цифровом следе вашей активности.

ClickHouse - это самая быстрая MPP СУБД, которую я на сегодня встречал за 15 лет работы в условиях, когда нужно делать расчеты с использованием больших массивов данных на лету.

Ее разработал Yandex для своих нужд, в первую очередь под те кейсы, которые я укажу далее по тексту. Со временем Яндекс поделился ею с миром на безвозмездной основе. А позднее вывел ее в отдельную организацию, зарегистрированную кажись в Голландии. Платформа оказалось настолько хороша, что ее стали применять повсеместно, от Alibaba до FAANG.

Прежде чем продолжить раскрою термины, ведь не все тут технари или работают в качестве инженеров/архитекторов данных:

· MPP – массивно параллельный процессинг

· Процессинг – различные операции и расчеты в базе данных

· СУБД – система управления базами данных

· SRE – site relation engineer - это DevOPS + современный мониторинг на векторах и разработка, в методологии от Google. У них есть только SRE, а DevOPS они не жалуют. В пользу SRE скажу, что во всех современных и продвинутых компаниях отказались от DevOPS в пользу SRE. Теперь, когда об SRE понятно, в деталях кто такие DevOPS:

· Транзакция – серия мелких последовательных операций в базе данных

· ACID (от англ. atomicity, consistency, isolation, durability) — набор требований к транзакционной системе, обеспечивающий наиболее надёжную и предсказуемую работу — по русски ACID - атомарность, согласованность, изоляция, устойчивость

· OLTP – Online Transaction Processing, т.е. процессинг транзакций в онлайне

· Primary key (PK)– в базах первичный ключ, а для обычных ACID баз – еще и уникальный идентификатор.

· Первичный ключ в таблице – это поле, где идентификатор является хозяином, т.е. впервые зарождается, он может быть использован в других таблицах в качестве ссылки, в таком случае, будет уже называться вторичным ключом, т.е. повторно использованным.

· Хэш сумма - преобразование массива входных данных произвольной длины в выходную битовую строку установленной длины, выполняемое определённым алгоритмом. Мы, для генерации уникального идентификатора, как правило используем MD5, т.к. из широкого диапазона входных значений md5 генерирует с высокой вероятностью уникальное значение и при этом относительно немного требует вычислительных ресурсов. А вот для вычисления изменений по всей сумме атрибутов таблицы лучше применять CRC32, т.к. он на порядки меньше ест ресурсов CPU и достаточен для высокой вероятности изменений в общей сумме десятков и сотне полей.

· Атрибут или поле – это колонка в таблице БД, как колонка в таблице excel.

· Кластер – несколько вычислительных узлов, объединенных в одну логическую единицу для выполнения распределенных вычислений. Применяют в 2х случаях: обеспечение надежности и для линейной масштабируемости вычислительных мощностей

Возвращаясь к CH.

В рамках разработки курса Data engineer для школы программирования Attractor School я выбрал в качестве MPP платформы CH, тогда и познакомился, настолько проникся простой установкой, и феноменально быстрой работой, что последующий некоторый геморрой с CH, специфичный для open source проектов, меня пока не разочаровал.

И я начал спрашивать, подойдет ли CH под DWH задачи. Читал статьи, слушал подкасты, разговаривал с коллегами, кто уехал на запад и работает с CH.

Отпугивало многое, что вот, мол, только под конкретные кейсы подходит:

1) Непрерывная запись гигантских массивов логов

2) Запись ивентов

3) вычисление агрегаций под векторную графику (применяется в онлайн мониторинговых системах типа Grafana)

и все в таком духе

Что поддержка есть только стандарта SQL89 (большинство SQL платформ поддерживает SQL 2008 и выше), в частности не укладывалось, как в ней нет команд на обновление (update) и удаление данных (delete)? И хотя не так давно эти команды появились, но работают они асинхронно, т.е. запустив, ты не знаешь отработалось или нет, нет коммитов (применения изменений из временного состояния, доступного только одному пользователю, до изменений, доступных всем).

Мы, при построении платформ данных, никогда не использовали сценарии truncate->insert (полная очистка - > новая заливка) данных в таблицах.

Всегда использовали различные методы вычисления дельты (пакет измененных данных)

И всегда садили во все цепочки таблиц платформы данных только дельту.

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

Но это происходило, главным образом, за счет корректно поставленной архитектуры, код ревью и воли на учение подаванов (джунов и миддлов). А архитектура предполагала использование большого количества join и update.

Как же теперь это реализовать в CH? Без Update и без join (CH join тоже не любит).

Вот об этом я и хочу рассказать. Точнее о том, как обновлять и удалять данные в базе, где такие команды не рекомендуется использовать.

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

Пояснил как они обновляют данные и на практике мы это реализовали и так к этому привыкли, что я теперь понимаю, в варианте от CH – это круче и намного удобнее, чем в прочих MPP!

Реализовали большой проект LakeHouse на инфраструктуре AWS, где построили кластер ClickHouse.

В CH существует небольшое количество движков таблиц (engine), каждый из которых обладают определенными свойствами, подходящими под нужные кейсы.

Не буду перечислять все, пока ограничусь главным, который используем мы: ReplacingMergeTree()

Главное, что вам нужно сделать в таком движке – это правильно определить Primary key (ключ идентификации записи в таблице). Если вы работали с OLTP Базами (обычные ACID транзакционные базы типа Oracle, MariaDB,Postgres, предназначенные под высокую скорость выполнения транзакций, скажем 50 транзакций в секунду), то там PK обязательно уникальный, в аналитических базах PK не обязательно уникальный, но в случае с replacingMergeTree (а для кластера replicatedReplacingMergeTree)вам нужно иметь уникальный идентификатор. Допустим у вас такого нет, в таком случае генерируете хэш сумму из набора полей идентификаторов, определяющих уникальность записи и получаете свой PK.

Например,

CREATE TABLE myFirstReplacingMT
(
`key` Int64,
`someCol` String,
`eventTime` DateTime
)
ENGINE = ReplacingMergeTree
ORDER BY key;

Теперь при сценарии вставки данных вы указываете команду, как и во всех прочих базах:

INSERT INTO myFirstReplacingMT Values (1, 'first', '2020-01-01 01:01:01');

В базе появится запись со значением ‘first’ в колонке someCol

На след день вы решили изменить значение на second, как это сделать?

В postgres и других СУБД вы бы сделали так:

Update myFirstReplacingMT set somCol=’second’ where key=1

Но как мы помним в CH строго не рекомендуется применять update.

И не нужно! Благодаря движку replacingMergeTree вы снова делаете вставку вот так:

INSERT INTO myFirstReplacingMT Values (1, 'second', '2020-01-01 00:00:00');

Как вы считаете, теперь в таблице будет 2 записи?

Вы правы, какое-то время будет 2 записи, но потом они схлопнуться и останется только одна – самая последняя. Но что делать, если вам нужно сразу прочитать таблицу и получить последнее значение, в таком случае добавляете ключевое слово FINAL в конце вашего скрипта, например вот так:

SELECT * FROM myFirstReplacingMT FINAL;

Получаем результат:
┌─key─┬─someCol─┬───────────eventTime─┐
│ 1 │ second │ 2020-01-01 00:00:00 │
└─────┴─────────┴────────────┘

Чтоб не прописывать везде final один раз напишите скрипт, который сгенерит для всех таблиц replacingMergeTree() одноименные представления с чтением данных исходных таблиц и final записей, например,

Create view vw_myFirstReplacingMT as myFirstReplacingMT FINAL

Тоже самое для удаления

Добавляете новое поле в таблицу – признак удаления, к примеру is_delete и далее вставляете запись с заполненным признаком, вот так:

INSERT INTO myFirstReplacingMT (key, someCol, eventTime,is_delete) Values (1,'second', '2020-01-01 00:00:00',1);

Да и тут придется учитывать удаленные записи дописывая условия вот так:

SELECT * FROM myFirstReplacingMT FINAL where is_delete=1

Но опять же, для всех таблиц, где вам важно поддерживать состояние удаления, в генерируемых представлениях просто предусмотрите это условие и вам в прочих скриптах не придется отдельно его каждый раз прописывать.

Какие плюсы от обновления данных таким способом:

Значительно упрощается логика операций, вы всегда просто делаете insert

работает очень быстро, операции update\delete в любой СУБД работают в 10-1000+ раз медленней, чем insert.

Минус: вам придется всегда собирать все атрибуты для таблицы, неважно что изменилось, но если вы будете вставлять только измененный атрибут, то прочие останутся пустыми и, таким образом, данные будут затерты после слияния.

Отмечу, что facebook и прочие системы, где генерируются колоссальные массивы данных работают по тому же принципу: данные никогда не удаляются

Например, вы удалили свой аккаунт и думаете ФСБ КНБ ЦРУ или ФБР о вас не узнают? Шиш.

Всегда пишутся новые сверх, или рядом в другую таблицу с отметкой, что вы удалили профиль. Система увидит эту отметку и перестанет для вас и других пользователей показывать ваш профиль, но для сотрудников FB и спецслужб ваши данные всегда будут доступны. Следы, что вы оставили в цифровой среде, от компетентных органов никогда не удастся скрыть (другое дело, что в компетентных органах как правило сидят специалисты, которые ничего не умеют кроме как «отнять и поделить»). Вы создаете ощущение спокойствия, очищая ваши переписки, профили и прочую инфу с соц. сетей, но будьте уверены, все это остается на серваках и ждет часа Х, чтобы сыграть против вас.

Намедни мне утверждали, что истерия на западе таких масштабов достигла, что от CH массово отказываются. Если это так, дайте знать в комментах. Пока не верю. Слишком хороша СУБД, чтобы идти по пути мракобесия

clickHouse потрясающа , и мне есть с чем ее сравнить:

redshift, vertica, Exadata. Со всеми работал и они все чем-то хороши, но CH доставляет эстетическое удовольствие, проведу аналогию: можно ездить на Toyota camry, а можно на Subaru STI или Porsche 911. Еще много вкусностей есть в CH, но пока на этом все.