July 6, 2017

Всё по кругу. 2/3. ClickHouse / kdb

На работе услышал о новинке - ClickHouse. Ну разумеется несколько бенчмарков, и, кажется, удалось посмотреть на kdb как бы со стороны.

Хотя CH и не кажется чем-то сверхъестественным, и даже не написан на тайном языке Q/K, но с другой стороны поразило, как _современный_подход_ и _адекватная_архитектура_ с ходу решилa большинство преблем, которые kdb, окопавшись в своём мире, имела много лет, и только около года назад, городя конструкции из говна и палок, пытается изобразить что он не просто awk, только векторный.

С другой стороны я привык к kdb, и думаю в в глубине души был бы даже рад, если бы кто в меня вернул веру что kdb + q побеждают всех :)

1) Многопоточность.
KDB однопоточный (кроме чтения с дисков). Это выдаётся как некое "мы и так лучше", но по факту это значит, что пользователи на гейте стояли в очереди и ожидают пока обслужат. Целый гигатский сервер стоит и не занят чем-то особо важным, пока пользовательский запрос не пробежит по ветке процессов и не вернётся назад. Недавно это немного поменяли - запросы хотя бы и уходят в систему асинхронно, но по-прежнему каждый инстанс обрабатывает только по одному из очереди. Ладно, kdb придуман так, что всё должно быть настолько быстро, что якобы и один поток не проблема, но всё не может быть быстро - есть чтение с дисков, есть агрегации на промежуточных процессах. Многократным дублированием этого всего изображается некая как бы многопоточность.

В CH всё кажется уже готово - если есть свободные нитки, то они используются и для чтения, и для распределённой агрегации.

2) Многопоточность.
Если позволять кому-то в kdb залазить своими руками, то всегда надо быть готовым к тому, что кто-то либо отъест всю память (без -w), но скорее он просто заблокирует надолго процесс, и все будут ждать пока он не отвалится по тайм-ауту через много минут.

В CH такой проблемы нет. Да, юзер забьёт одну нитку, другой - вторую, но, если это не какие-то рвущиеся в дверь наркоманы, то система останется работоспособной для всех остальных. В CH даже появятся пулы ресурсов: CPU, disk, net

3) Индексация.
У kdb для диска один индекс `p# - это по сути группировка блоками по этом индексу + сохранние указателя на первый элемент блока. Да, в некоторые случаях это ok, но в реальности случаи сложнее. Как только начинается выборка за пределами индексированного поля, то всё уже не так радостно.

В CH есть составные индексы. Можно не только по одному полю выбирать нормально. С гранулярностью, т.е. и места особо не занимают.

4) Q in-mem.
Вот это интересно думаю. Когда ты пишешь на K, то это по сути обработка простых структур в памяти. Можно сделать довольно эффективно, но для чтения с диска такое не пойдёт - с диска только через select на Q. Разумеется писать разные запросы для памяти и диска никто не будет. Kdb ничего оптимизировать не будет. Итого, какая-нибудь аггрегирующая функция, да хоть last, с группировкой "select last v by k from t", сначала сгенерит для каждого ключа отдельный вектор, покопирует туда соответсвующие группировке данные, а потом выберет последний элемент. Я не знаю как оно там совсем внутри работает (а кто знает?), но select с диска при этом умудряется быстрее отработать. Но именно скорость in-mem продаётся как что-то сверхъестественное. Я с этим не спорю, если писать на моём любимом K или хотя бы изображать это на Q и из памяти- то да, быстро будет, но так писать никто не будет.

В CH есть тип таблицы Memory, потенциально это могло бы накрыть in-mem в kdb, но тут не поддерживаются никакие индексы, да и в целом решение с кешом кажется удобнее. Помимо дискового, в CH есть свой кеш, что можно использовать как in-mem, но только он LVC и нет механизма принудительно там данные удерживать. Но, судя по гугл-группe, какие-то скрытые kdb'шники в группе кликхауса хотят того же. + к этому стоит добавить, что если это простая выборка _только_ по индексам, то kdb выдаёт её за время близкое к нулю, CH такое время не показывает, но причины не искал: может быть обработка запроса, может гранулярность индекса (в kdb `g# в памяти храние все индексы для ключа), а может передача в клиент.

Опять же в kdb - память для данных бывало и заканчивалась, вместе с работой приложения и S1 по сути. С кешом такого произойти не может - да, может замедлиться.

5) Языки. Статическая/динамическая типизация.
Тут уже всё написал thedeemon, я тогда ещё был в жж - http://thedeemon.livejournal.com/54732.html
Да, писать на K и даже на Q удобно и быстро, но оно никуда не развивается. Было и такое, что какие-то данные на входе оборчивались в списки списков, при этом эти данные и принимались и как-то умудрялись обработаться, при этом ломая существующие структуры и частично теряясь. Ес-но в статике такого не было бы. Я бы оставил диалект Q только как какую-то надстройку для пользователей, вроде как и сделал maxim. Видимо надо ещё раз попытаться имитировать K в нормальной системе типов. Так как в основном реальный код на K/Q не является только красивым однострочником, а однострочник можно и переписать на чём-то достаточно функциональном - ML, Haskell, ?Rust?

Но о языках видимо в третьей части, которую ещё не придумал.