Как стать автором
Обновить
95
0
Алексей @o6CuFl2Q

Разработчик

Отправить сообщение
Видео с конференции Saint HighLoad++ 2019: youtu.be/2iR7i4akL44
В системе есть очень много источников недетерминированности: turbo boost, thermal и power throttling, page cache, memory layout, зависящий от аллокатора, page faults, фрагментация физической памяти, расположение данных на конкретном месте на HDD, многопоточное выполнение — когда результат всегда корректный, но скорость работы может меняться в широких пределах… Туда же можно отнести profile-guided optimizations, JIT и machine learned data structures. Всё это не так уж плохо, пока скорость работы меняется всего лишь в разы или меняется хотя бы предсказуемым образом.

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

Я слышал про специализированные базы данных для RTOS, но ничего про них не знаю.
Да, смотрели. В самом начале в ClickHouse был тип данных VarInt, VarUInt. Но он использовал обычное кодирование по одному числу (такое же как в Protobuf). Оно работало не очень быстро, поэтому его удалили. Само собой напрашивается использование вариантов group-varint, которые обрабатывают блоки из некоторого количества чисел одновременно, как тот, что по ссылке. Мы этого довольно долго не могли сделать, так как не хватало фичи — возможность указывать отдельные кодеки для отдельных столбцов (всё-таки, это — специализированный алгоритм и тут лучше пользователю самому указать, для каких столбцов его использовать, а для каких — нет). В начале этого года возможность указания кодеков для отдельных столбцов появилась, но сам кодек ещё не добавили, хотя теперь этому ничего не мешает.
LZ4 умеет обнаруживать совпадения длиной минимум 4 байта. Но в последовательности чисел UInt32 0, 1, 2… (записанных в бинарном виде) нет повторяющихся подпоследовательностей такой длины, поэтому никаких совпадений не находится.

Для сравнения, если так записать числа UInt64, то они будут кое-как сжиматься (например, в начале повторяются фрагменты из 7 нулевых байт).

Более правильное решение — добавить кодек Delta перед LZ4. Недавно в ClickHouse появилась такая возможность — настраиваемые кодеки для отдельных столбцов, которые можно выстраивать в цепочки.
У меня тоже — поэтому такой вариант сразу отбросили. Даже если сделаем, то как это потом поддерживать?
Да, рассматривались. В старых реализациях memcpy именно они используются (а ещё используются в реализации копирования из ядра Linux в userspace — почему?). Казалось бы, это как раз то, что нужно — вся работа в одной инструкции.

Но в современных реализациях вместо этого написано куча кода с векторными инструкциями, разворачиванием цикла, duff device, prefetch, non-temporary stores. Почему-то так получается эффективнее. Хотя что мешает процессору делать так, чтобы REP MOVSB выполнялось так же эффективно?
Кстати, на ближайшей конференции Highload++ Siberia тоже будет доклад — про оптимизацию поиска строк. На первом скриншоте «VolnitskyBase» имеет некоторое отношение к этому докладу :)
Это действительно так, но я бы предпочёл встроить буфер непосредственно в MergeTree, чтобы избежать сразу всех проблем отдельных Buffer таблиц.

Для сохранения порядка данных и отсутствия блокировок при чтении, можно использовать такую же схему (иммутабельные куски) как в MergeTree, но для данных в оперативке.

С помощью табличных настроек можно было бы управлять такими параметрами как: наличие или отсутствие WAL; частота применения fsync для WAL и необходимость его ожидания; возможность синхронно дождаться сброса буфера в основную таблицу.
Проблема с ALTER таблиц типа Buffer сейчас решается: github.com/yandex/ClickHouse/pull/3603
Ссылка устарела.
Она не прошла мимо внимания, а даже немного его зацепила :)
Но подробно изучить действительно не успел. К тому же не было возможности вместить всё в рамки 40-минутной презентации (по факту получилось около часа, и то пришлось резать).

Было бы хорошо иметь не только поверхностный взгляд, а хотя бы чуть-чуть ознакомиться с системой. Но мой доклад совершенно несерьёзный и нацелен скорее на расширение кругозора — чтобы просто дать повод что-нибудь посмотреть.
Нет, не специально. Вот презентация со всеми ссылками: yandex.github.io/clickhouse-presentations/database_saturday_2018/#
Прошу простить за поздний ответ — я почему-то только сейчас нашёл эту статью.

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


В статье ошибка. Как раз смысл отдельной поддержки сэмплирования в ClickHouse в том, что при этом с диска читается меньше данных. Данные расположены на диске так, что для чтения сэмпла достаточно прочитать отдельные диапазоны данных. Это реализовано очень просто: данные хранятся в небольшом наборе кусков, каждый из которых упорядочен по первичному ключу. А ключ сэмплирования обязан входить в первичный ключ, при чём это имеет смысл только если до него присутствуют лишь столбцы небольшой кардинальности. Для примера, первичным ключом может быть (CounterID, Date, intHash32(UserID)), а ключом сэмплирования — intHash32(UserID).

Подробнее здесь и здесь .

В итоге, колоночная СУБД для вашего запроса собирает полные строки исходных данных — не является ли overkill'ом использование колоночной БД в этом случае?


Загружать данные в ClickHouse только для применения модели, по большей части бессмысленно. Основной сценарий использования данной фичи — когда данные уже хранятся в ClickHouse. В этом случае удобно иметь модель в виде готовой функции, которую можно использовать прямо в SELECT-е, вместо того, чтобы выгружать данные и затем обрабатывать их скриптом.

Для примера, у нас хранятся данные Яндекс.Метрики, и мы применяли модель на таблице с сессиями пользователей, для предсказания покупки в интернет-магазине. В таблице с сессиями уже есть всевозможные свойства пользователя, включая данные об истории (например, сколько сессий на сайте было раньше). Дальше мы берём из этого всё, что кажется подходящим, и обучаем модель. После обучения можно выкинуть из модели большинство факторов.

Вопрос по существу 2: вы используете свой XML-подобный формат для хранения моделей. Почему не PMML, являющийся стандартом де-факто в индустрии?


На этот вопрос не могу ответить, так как далёк от разработки как самого CatBoost, так и его интеграции с ClickHouse. Спрошу у коллег…
Да, термин «колоночная» более распространён. Никакой разницы в этих терминах нет, имеется ввиду одно и то же.
А метрика у вас как раз ведь на ClickHouse работает?

Да.

Когда почините глюки с запросом больших колличеств статистических данных? Глюк очень просто воспроизвести — переходим в метрику, выбираем детализацию по дням, а затем период статистики в пару лет и… Вуаля! Вместо данных шляпа. Хотя иногда почему-то отрабатывает нормально. Похоже в случаях, когда до первого запроса большого периода использовалась детализация «по неделям», но не уверен.

Не могу подробно прокомментировать конкретно вашу проблему. Если коллеги из Метрики найдут ваше обращение в support, подскажут номер счётчика, то мы сможем поискать медленные запросы в query_log.

Пока у меня только есть некоторые догадки — например, я знаю, что несколько отчётов плохо работают из-за cache словарей из MySQL (при этом для MySQL используется слабый кластер без SSD) — для решения проблемы мы реализовали словари из shared library, которые идут напрямую в YT. Но эта возможность ещё не используется. Надеюсь, что станет лучше.

Возможно совпадение, но вроде бы стало работать чуть лучше как раз примерно в то же время что и вышла статья, где вас проверяли в PVS-Studio =)

Это никак не связано. Впрочем я буду рад, если больше людей будут изучать и проверять наш код.
Спасибо! Я никак не мог понять, почему появилось два инструмента, и документации, где было бы об этом написано, не нашёл.
Самый простой пример:

template <typename T>
void f(T x)
{
    if (x < 0)  /// Код будет полностью удалён, если x - unsigned.
    {
        ...
    }
    ...
}
...

int x;
f(x);

unsigned y;
f(y);


Реальный пример из практики:

github.com/yandex/ClickHouse/blob/ff1598c8d12970c9bb76d3f0fd6bb8a22471b7ae/dbms/src/Interpreters/Aggregator.cpp#L596

Смотрите на no_more_keys и Method::no_consecutive_keys_optimization.
Это всё bool, известный на этапе компиляции.

В C++17 это можно написать более явно с помощью if constexpr, впрочем область применения if constexpr больше.

Кстати, в некоторых случаях для достижения такого же эффекта даже не обязательно использовать шаблоны. Можно передать обычный параметр в функцию, понадеясь на то, что компилятор будет достаточно умён, чтобы применить оптимизацию — клонирование функций после constant propagation (см. параметр -fipa-cp-clone в gcc, входит в -O3).
Спасибо, анализ полезный! Поправили указанные ошибки:
github.com/yandex/ClickHouse/pull/1204

(Одной из ошибок уже не было в master, так как анализировалась более старая версия кода.)
Хорошая практика — положить домен от URL-а в отдельный столбец. Так последний запрос будет намного быстрее работать.

Информация

В рейтинге
Не участвует
Откуда
Россия
Работает в
Зарегистрирован
Активность