А пришло время проговорить про колоночное хранилище. Про это нам расскажет Влад Буйвол. Попросим из Яндекса. — Всем привет. А начну с того, что современные продуктовые сервисы почти все зависят от пользовательских данных. И работа с этими данными не ограничивается одним лишь надёжным хранением. Так или иначе, мы приходим к задачам аналитики. Мы хотим понимать поведение пользователей, оценивать эффективность новых фичей, а, строить какие-то прогнозы и всё это делается на основании реальных цифр, а не предположений. И, как правило, под эти задачи и используются колоночные SBD или так называемые аналитические, а, позволяющие за секунды агрегировать гигабайты данных. А, но что же делает СУBD аналитической и причём тут вообще колонки? Постараемся разобраться. А, расскажу сперва немного о себе. Я занимаюсь разработкой аналитических инструментов в системе YTurus или IT. YT - это Open source проект для хранения и обработки больших объёмов данных, разработанный в Яндексе и использующийся в той или иной степени в большинстве сервисах компаний. А, несмотря на тот факт, что вычислительная часть платформы изначально была основана на Mapрес парадигме, за время её существования появилось несколько новых инструментов для работы с данными, среди которых OLTP движок под названием динамические таблицы и нативные интеграции с Кликхаусом и спарком. А Кликхаус, в свою очередь, э также является разработкой Яндекса и также стал асорсным проектом. И это один из самых популярных представителей аналитических СБД. А, и, в частности, я с коллегами работаю над про над продуктом, который называется Click House Over YT. А, и лечить. А по факту это доработки в ванильный кликхаусный сервер, чтобы научить его работать поверх нашего распределённого хранилища. А, и это и подводит нас к теме моего доклада. Мы разберёмся, как фундаментальные решения о хранении данных могут, а, повлиять на дизайн всей СУД и какие практические преимущества это может нам принести. А, несмотря на тот, несмотря на то, что прошлый доклад уже в какой-то степени осветил базу по СБД, а, упосионны там рас классические реляционные, упомянем вкратце, что такое SBD и какие задачи она решает. А СБД - это система управления базами данных, является основным компонентом в дизайне большинства сервисов и берёт на себя задачи хранения, а, и предоставления удобного и быстрого интерфейса для работы и доступа с вашими данными. А, на мой взгляд, важно понимать, что это, в первую очередь тоже система, и она имеет свой внутренний дизайн, архитектуру, а, который можно показать такой простой иерархией, где в основании у нас находится, а, слой хранения или так называемый storage engine, с которым непосредственно работает уже потом движок исполнения запросов. И на самой верхушке находится какой-то простой фронт, принимающий запросы и обрабатывающий их. А, в частности, мы будем всё внимание уделять слою хранения. А сперва давайте ответим на вопрос, что помогает СУБД трактовать ту или иную последовательность BТ как некоторый, а, объект с внутренней структурой и логическими взаимосвязями между этими объектами. А в теории СУБД, а это позволяет нам делать модель данных, а которая по факту представляет из себя набор определений и концепций для описания структуры хранимых данных и типов взаимосвязий между ними. А также с моделью данных тесно рассматривают такое определение, как схема данных, которая уже позволяет описать конкретную коллекцию в рамках выбранной модели. А за время развития СУБД появилось немало моделей, и каждая из них решает свою задачу. А, и, в частности, на прошлом докладе мы упоминали реляционные, и про них же и будем говорить сейчас. А реляционная модель, как и все остальные модели, предоставляет некоторую логическую абстракцию вашей базы данных, которая удобна как для пользователя базы данных, так и для её разработчиков. А, и в частности реляционная модель основанная на таких простых структурах, которые называются отношения. А отношением в терминах реаляционной модели является некоторое множество, а, содержащее взаимосвязи атрибутов, которые описывают некоторую сущность. А, и также рассматривают такую вещь, как кортеж. Это уже набор скалярных значений атрибутов. Кортеж или тюпол. А, говоря простыми словами, отношения - это не что иное, как табличка, где колонки у нас - это атрибуты, а каждая строчка - это кортеж. И поскольку отношение - это в первую очередь множество, а для работы с ними мы получаем большой богатый математический аппарат алгебры множеств, а в частности такие операции, как
Segment 2 (05:00 - 10:00)
объединение множеств, пересечение или их произведение. А следующей важной характеристикой, которая также уже было упомянуто в рамках прошлого доклада, является тип нагрузки, под который была оптимизирована система. А, и тут разделяют два таких существенных класса. Это системы, оптимизированные под в WR heavy нагрузку, то есть под пишущие запросы, и аналитические системы, которые преимущественно исполняют крупные читающие запросы поверх большого объёма данных. А стоит также отметить, что есть некоторая промежуточная промежуточный класс гибридных моделей, да, — так называемый. — Довольно популярная сейчас тематика, все в неё движутся, в частности, крупные allп игроки. А, но поскольку это довольно глубокая тема, мы её упомянем лишь в школь. — Сегодня про АAB, — да? Сегодня вот прошлое было преимущественно, прошлый доклад преимущественно был про OLTP, а сейчас мы поговорим про АAB. А также следующим, следующая характеристикой является память. А при работе с данными вы так или иначе придёте к работе с памятью, и современные архитектуры предоставляют нам целую иерархию типов памяти, взаимодействующих друг с другом, а от очень быстрых, но маленьких по объёму до больших по объёму, но медленных. А, в частности, вот можем увидеть такую табличку с числами, которые должен знать каждый разработчик, с временами доступа для разных уровней памяти. И если мы это всё отнормируем по L1 кэшу, а видно, что доступ к внутренней памяти, а, или так называемой оперативной, и к внешнему диску - это довольно существенные величины, а, которые надо учитывать. И в разработке СУD выделяют такие два класса, как inmemory базы, которые хранят, э, все данные преимущественно в оперативной памяти, а и которые, как правило, направлены на задачи с быстрым доступом к вашим хранимым данных. И, а, системы с внешним диском, а, и, как правило, аналитические нагрузки подразумевают работу с диском, потому что объёмы ваших данных измеряются, как правило, терабайтами. А, и поэтому, учитывая долгое время доступа, нам нужно более качественно продумывать, как мы с этим всем будем работать. — Здесь, наверное, стоит пояснить, что L1 кэш не за секунду отвечает. Это просто это просто Да, да. Вот на самом деле цифры вот такие. А это просто иллюстрация того, насколько быстро растёт время доступа, если вот за секунду взять L1 кэш. И мы видим уже вот переключи, где там уже что это что оперативная память уже 100 секунд. Понимаете, как короче брос жесть. А SSD 4 часа. Ну просто вот записать байтик 4 часа бы у вас заняло, понимаете? То есть как ну здесь именно об этом речь. Да. — Да. Благодарю. А мы уже зафиксировали, что данные в случае аналитической системы у нас лежат на диске. А знаем, как наши данные логически представлены, что это какие-то таблички. А сейчас мы хотим разобраться с тем, как нам эти таблички физически представить на диске. А, и для этого используется модель хранения, а которая, в частности, и задаёт взаимное расположение ваших атрибутов э на диске. А поскольку у нас есть разные классы систем, а, оптимизированных под читающую или подпишущую нагрузку, соответственно модель хранения может довольно сильно сказываться на производительности таких систем. И небольшой спойлер, что это влияет на весь дизайн остальной системы. А, но какие же бывают вообще модели? И прежде мы определимся с тем, как наша табличка будет представлена на диске. Как правило, таблица реляционная, а, представляет из себя несколько там файлов на вашей файловой системе, и при этом каждый файл ещё логически подразбит на такие сущности, которые называются страницами. А, да, такая гранулярность вызвана тем, что, а, диски физические так устроены, что обращение к конкретному адресу на самом деле повлечёт за собой чтение некоторого блока. А, как правило, в среднем это 4 Кб. И такая гранулярность, как я сказал, файлов нацелена на то, чтобы, а, больше более нативное чтение получить, то есть идти в ногу с диском, как работает он физически. — Ну да. То есть файловые у файловых систем такие традиции, скажем, по-простому, да, что работ они работают сницами. — Ну, можно так тоже сказать. Спасибо. А, и теперь перейдём к моделям хранения. И первой из них является Enerry Storage Model или сокращённая NSM. А наиболее логически такая понятная модель
Segment 3 (10:00 - 15:00)
поскольку мы берём и каждую строчку нашей таблицы последовательно записываем а в рамках страницы. А утверждается, что эта модель идеально подходит для OLTP нагрузок, где у нас, как правило, точечные запросы и также запросы вставки, обновления и удаления. А представим, что у нас есть некоторая табличка с пятью атрибутами, а которая задаёт там описывает активность какого-то пользователя в сети и его там метод, что он делает с конкретным хостом. А в случае NSM в рамках страницы нашего файла у нас будет просто последовать Так. Ой, вот он, я потерял курсор. А вот а просто последовательная укладка наших атрибутов. А представим, что у нас вот есть какой-то наш точечный запрос. Под точностью подразумевается то, что а в результате запроса у нас будет один кортеж. И в случае NСM-хранения мы всегда можем точно определить локацию этого кортеджа на странице, поскольку все атрибуты у нас лежат последовательно. А в случае же запроса записи, всё, что нам нужно сделать - это, грубо говоря, добавить в конец текущей страницы или там начать новую. А, но что будет, если у нас в нашу систему придёт следующий запрос, где мы хотим, а, посчитать а количество запросов от уникальных логинов на hostname с префиксом Яндекс. А, попробуем промоделировать. В первую очередь нам нужно будет извлечь все хостнеймы для того, чтобы произвести фильтрацию, а в последующем все логины для того, чтобы уже доагрегировать и сгруппировать. Но все остальные данные будут просто бесполезно загружены в память с диска. А, и это не единственная проблема, поскольку, а, при обращении к хостнеймам нам придётся нелокально, а, совершать операции доступа к памяти, чтобы извлечь каждый следующий hostname, грубо говоря, прыгать по памяти, что не совсем, а, ложится, ну, не совсем дружит с коэшами процессора. А, таким образом, усм-моделей есть преимущество быстрой вставки, а, удаления и обновления. И они подходят для точечных запросов, когда нам нужно извлечь весь кортеж. Но, как мы увидели, для запросов, затрагивающих большую часть атрибутов а нашей таблицы, а, мы вынуждены не локально обращаться к памяти, да ещё и к тому же запрашивать много данных, которые нам и не нужны были. А логическим решением этой проблемы является транспонированное, так сказать, хранение или так называемый decomposition storage model DSM, а где мы храним последовательно значение одного конкретного атрибута. А при этом, поскольку а модель поскольку модель данных предоставляет нам абстракцию по о том, как выглядят наши данные, то есть таблица, а СУBD забирает на себя ответственность по объединению и разбиению атрибутов этого кортежа. А в случае DSM а наша табличка будет выглядеть следующим образом. То есть каждая страничка у нас будет отвечать за хранение какого-то конкретного атрибута. А, и тогда наш всё тот же аналитический запрос, а, просто сперва вычитает страничку, на которой хранятся все хостнеймы, произведёт фильтрацию в памяти, что будет casшфendly, поскольку мы просто будем последовательно обращаться к каждому элементу массива. А, и затем, зная уже индексы конкретных кортежей, которые нам нужны, мы просто вычитаем следующую страничку с логинами и извлечём необходимые логины для дальнейшей агрегации. Таким образом, ДСM предоставляет нам преимущество того, что мы не читаем лишних колонок, и мы ускоряем работу с нашими данными в памяти, поскольку получаем локальность данных. Однако теряем всё то, что имели на NSM-модели с точечными обращениями к кортежам и простотой вставки и обновлений. А, таким образом, вот эти проблемы, которые мы озвучили, а что NСM неэффективен для аналитики, а ДСM неэффективен для точных запросов вставки и чтения. А при этом у нас есть следующее наблюдение, что Оlab запросы почти никогда не обращаются к одной колонке. В какой-то момент мы будем вынуждены собрать там весь наш кортеж таблицы или большую его часть. А при этом мы всё ещё хотим хранить данные в колонках, поскольку это, а, выгодно с точки зрения и операций, а также последующего исполнения, а, в уже в самой системе. Но при этом нам очень нравится преимущество NSМ-моделей, где у
Segment 4 (15:00 - 20:00)
нас имеется пространственная локальность атрибутов одного кортежа. А, таким образом пришли к третьей гибридной модели, которая называется Partition Attribute Across, а, или PX, а, которая совмещает в себя, так сказать, две ранее упомянутые модели. А у данной а модели хранения есть популярные форматы, открытые форматы, использующие в большинстве систем, а, которые реализуют их. Это паркет, org и arrow. Почти наверняка в какой-то степени вы когда-то могли о них слышать. может быть даже уже и работать, потому что популярная штука реально. Ну, как быть — весь на паркете стоит, что называется. — А как же всё физически будет выглядеть в случае этой модели? А, грубо говоря, у нас есть наша таблица. Мы берём некоторую последовательность строк, а, в данном случае по три, а, и разбиваем их на так называемые rowу-группы. И уже каждую роу группу мы будем хранить на отдельной страничке, но в рамках страницы мы будем хранить атрибуты наши последовательно. И а последовательность атрибутов, значение атрибутов будет называться colмчан. следующий слайд. Так, а модель хранения помогает нам не читать лишних колонок, но мы всё ещё а остаёмся с проблемой того, что а ио является и к внешнему дискуляется самой таким самым, а, узким местом, а, СУБД. И при этом мы хотели бы каким-то образом решить проблему того, что а мы часто обращаемся к диску и чтоб, ну, давайте, проще говоря, а попробуем больше читать за одну операцию. А таким образом мы быстро приходим к решению того, а чтобы сжимать наши страницы и тем самым повысить полезность читаемых данных за одну операцию. Однако приходим к ключевому компромиссу между затратами на CPU для последующей декомпрессии и степенью сжатия наших страниц. А наивным подходом тут будет использование каких-то традиционных алгоритмов вжатия напрямую к страничкам нашей базы, а в частности к ZIP, ЛЗ4 или ЗСD. А, но у такого подхода есть две существенные проблемы. А первая из которых формализуется как отсутствие эффективных обновлений. Под этим подразумевается тот факт, что для а каких-то изменений в рамках страницы нам необходимо будет полностью её вычитать, а рожать, изменить, сжать и записать назад, что даже на словах звучит дорого. А, а следующая проблема и наиболее важная, которую мы хотим решить - это ранняя материализация. Для того, чтобы нам хоть как-то работать с контентом нашей страницы, а нам нужно произвести декомпрессию этой страницы. А в идеале мы хотели бы прийти к какому-то такому промежуточному состоянию сжатых данных, поверх которых мы всё ещё можем исполнять наши запросы и а разжимать конкретные атрибуты лишь в конце для там а последних частей выполнения запроса или уже для вывода результата. А, и сейчас мы разберём пару подходов, которые используются для достижения таких целей. А таким самым простым и распространённым не только в мире SOBD является runcoding или RLE, когда мы сжимаем последовательность одинаковых значений атрибутов в тройке, а где в тройку входит само значение атрибута, его стартовая позиция и количество элементов. А, наверное, наиболее традиционно было бы увидеть значение атрибута и количества элементов, но а для аbd часто используется тройка для того, чтобы удобно бинарить поверх сжатых данных по стартовым позициям. А, и очевидный факт об этом подходе, что нам хотелось бы видеть отсортированную колонку для того, чтобы достичь большего коэффициента сжатия. А для примера вот у нас есть две колонки: айдишник и value, хранящий булево значение. И в случае сжатия мы получим там в половину меньше данных. Но если бы всё было ещё и отсортировано, это было бы вообще шикарно. Так, следующий подход называется beitппеcking. И его смысл в том, что зачастую для хранения целочисленных значений используются слишком широкие типы. А целочисленные, например, а мы хотим хранить там се по факту семь битов полезной информации, но зачем-то, ну, зачем-то используем IN32 и, а, там все, а, сем все 27 верхних битов нам попросту
Segment 5 (20:00 - 25:00)
не нужны. Так, почему бы не скомпоновать как-то наши значения, а, и хранить меньше битов. А, так. Следующий подход, э, - это дельтаэн encodдинг, э, когда в нашей колонке значения в целом довольно связаны друг с другом и представляют из себя там небольшие изменения относительно друг друга, тогда мы можем зафиксировать первое значение и для всех остальных хранить лишь дельты а между текущим и следующим элементом. И поверх этого, как можно понять, круто ещё и накрутить le можно и пожать всё ещё больше. А у этого подхода есть ещё и вариация под названием, а, Frame of Reference. А, и единственное различие тут в том, что в качестве базового значения, от которого мы берём дельты, мы фиксируем не первый элемент, о, а глобальный минимум по там порции хранимых данных. А, и наиболее используемый подход - это dictionary encoding. А, как правило, он используется тогда, когда у вашей колонки низкая кардинальность. И мы можем, ну, и это зачастую чаще всего строки. И мы можем просто записать там словарик из наших различных строк отдельно, а, и вместо фактических значений колонки хранить коды отображающи коды, которые являются отображением исходных значений. А на примере вот опять же строковых колонок, а у нас там есть колонка с именем, содержащая там всего четыре различных имени, а которые мы отобразили там в целочисленное значение, а можно в зависимости от кардинальности использовать там произвольно широкий int. А, но при этом на данном примере отображён важный факт, что мы хотим, а, сохранить, а, порядок сортировки кодов и исходных значений для того, чтобы достичь того самого эффекта, а что мы можем исполнять наши запросы поверх сжатых данных. А, например, как тут мы делаем запрос, где имя больше Ивана. И по факту это отображается в имя больше двадцати, учитывая представлен ранее словарик. А таким образом, резюмируя топик о сжатии данных, а можно понять, что выбор конкретного алгоритма очень тесно связан с природой самих данных и с их структурой. И довольно часто в системах это определяется в рантайме. И вполне нормально, что а в рамках одной колонки у вас будут находиться а страницы с разной кодировкой. И при этом третий важный факт, что а не обязательно использовать лишь одни вот эти представленные алгоритмы, которые позволяют нам работать сразу поверх сжатых данных. Довольно часто дополнительно всё ещё сжимается поверх представленными в качестве традиционных подходов сжатия кодеками для того, чтобы достичь большей компрессии. И мы можем сделать это вполне а просто поскольку у нас уже сжатые данные, а поэтому нам не нужно, а там сжимать их сильно, чтобы потом, а, тратить больше CPU тайма на их расшифровку. А, ну, мы пожали данные, расположили их как-то на диске, это всё хорошо, но диск всё ещё является узким местом. А, и такие важные наблюдения, что аналитические запросы довольно часто обладают большой селективностью. Это может быть там посчитать что-то за конкретный месяц или по конкретному пользователю. И понятно, что тянуть весь там терабайтный датасет в систему для того, чтобы выполнить такую простую агрегацию, нам бы не хотелось. Мы хотим знания о том, а что нужно конкретному запросу, в частности фильтр, каким-то образом а опустить до уровня слоя хранения и использовать это непосредственно при чтении данных с диска. На это направлен подход датаскиппинга. А, и самым таким простым методом в датаскиппинге являются статистики, в частности статистика. Например, мы хотим всё над той же нашей табличкой активностей произвести запрос, фильтрующий по таймстмпу. А для этого давайте при записи будем дополнительно считать MinМАК статистики по всем там целочисленным колонкам, ну или в целом по колонкам, для которых это имеет место быть, а, и хранить рядом со страницей. А там в хедере её. А тогда при таком запросе а мы можем не перед тем, как
Segment 6 (25:00 - 30:00)
читать саму страницу непосредственно заглянуть в эти нашик статистики и понять, что там половина страниц нам и не нужна вовсе, а вычитать только необходимые. А следующим часто использующимся подходом является применение blom фильтров. А BL фильтры - это такие простые в плане структуры, а для того, чтобы хранить их ещё рядом со страницей, а функции, позволяющие нам точно дать ответ на то, что в конкретном множестве отсутствует некоторый заданный элемент. Например, сейчас мы хотим пофильтровать по там конкретному хостнейму. А, и используя блумфильтры, мы можем узнать, что там на второй странице точно нету хостнейма, который мы ищем. А, но поскольку структура вероятностная, мы вполне можем наткнуться на случай, когда прочитаем страницу и там значения и не было. А, и третьим подходом является использование так называемых спарс- индексов. Это в некотором, в некоторой степени обобщение двух ранее озвученных подходов, а, но хранящееся преимущественно в inмеory и работающая ещё до того, как мы собираемся что-то читать. А, разобравшись со слоем хранения, перейдём а к движку исполнения запросов. И как и движок хранения, данная сущность работает с некоторой своей логической абстракции, которая называется план запроса. А по факту план запроса - это не что иное, как дерево в терминах теории графов, где а вершинами у нас являются некоторые операции и метаданные, необходимые для их исполнения. А, и корнем этого дерева будет тогда служить по факту наш результат запроса, а листья - это будут источники для данных конкретного запроса. Выделяют два типа планов: логический и физический. Первый из них является результатом некоторого первичного анализа и применения оптимизаций, реализованных в системе для упрощения вашего плана. А физический план - это уже, а, отображение логического, а он уже имеет конкретные, он уже в своих метаданных содержит конкретные алгоритмы, необходимые для применения, а также там набор инструкций, которые нужно выполнить прежде чем получить результат. Без ограничения общности рассмотрим некоторое поддерево нашего физического плана, которое занимается вычислением суммы трёх элементов, и попробуем промоделировать возможное исполнение такого плана в системе. А, например, у нас есть наш кортеж, и мы хотим посчитать сумму по трём его полям. А по факту исполнение такого выражения это будет ни что иное, как обход этого графа в глубину и исполнение конкретных операций. записанных вершинах. А такой подход носит название это time или итеративный. А, но важно помнить, что в рамках аналитических систем, что данные у нас представлены немного иным образом, а, в частности мы имеем колонки данных, а не а кортежи в целом. И применение итеративного подхода тут SS будет довольно плохо ложиться на архитектуру процессора, поскольку при обращении к конкретному значению колонки по факту мы загружаем в кэш-процессора некоторую последовательность значений. А но итеративный подход не пользуется этим свойством, а просто идёт и считает дерево дальше. А логическим таким исправлением этой проблемы будет факт того, что а давайте теперь в вершинах считать не просто там сумму двух значений, а сумму для двух там последовательностей вот значений этого атрибута. А, к счастью, для разработчиков СБД задача выполнения арифметических операций таких простых уже давно была формализована и реализована в процессе в процессоре под названием SIMD инструкции. А S расшифровывается как single instruction multiple data. А, и по факту, а, инструкции данного типа, а, делают ровно то, что мы и хотели бы применить в нашем подходе вычисления выражения поверх колонок. А они принимают на вход некоторый набор данных и за одну операцию считают а сумму, например, сразу для вот этих двух наборов. Таким образом, используя SIMT, достигается параллелизм уровня данных. А, и применив симвод инструкции
Segment 7 (30:00 - 35:00)
поверх озвученного ранее подхода вычисления нашего дерева, мы приходим к так называемому векторизованному движку, а, исполнения запросов. А, конечно, тут, ээ, остаётся за скобками много важных деталей о том, как разбира как выбирать размер порций данных, которые необходимо вычислить, а что делать с промежуточными данными. Но это всё адванс топики, которые а выходят немного за тайминги. А вот мы реализовали там, а, хорошую с точки зрения, а, оптимальности для аналитических запросов, ээ, модель хранения, реализовали векторизованный движок. Почему мы не можем поверх всего этого что-то навернуть и получить ещё, аэ, там систему, которая могла бы почелленджиться с там пазгрёй в OLTP нагрузках а или вообще с какими-нибудь другими OLTP системами? В частности, выделяются следующие проблемы. А первая проблема - это локальность данных. Когда мы обсуждали, а, проблему НСМ подхода для аналитических запросов, что данные у нас находятся нелокально в, ну, колонки у нас находятся нелокальны, и нам приходится прыгать по памяти, а хранение в колоночном виде имеет всё ту же проблему. Но теперь, если мы хотим обращаться к конкретному кортежу, нам придётся прыгать между колонками, а для того, чтобы собрать нашу строчку. это, в частности, плохо для OLTP нагрузок, потому что там довольно часто происходит конкретное обращение к строке. А следующей проблемой является мелкие обновления поверхну крупно сжатых блоков. АP нагрузки характерны частыми там чтениями, перезаписями какого-то подмножество атрибутов одного кортежа. А, но аналитические системы довольно часто стремятся хранить больше данных и в более сжатом виде. А, и проблема, которую мы озвучивали в наивных, а, подходах сжатия, присутствует теперь для обновления записей в аналитических системах. Потому что для того, чтобы выполнить нам такое обновление, нам нужно будет прочитать весь этот крупный блок, разжать его, изменить, сжать и записать назад. А, это дорого, так не хотим делать. и поэтому реализовывают некоторый промежуточный слой, э, типа дельта обновлений, который теперь нужно будет ещё и поддерживать, а, там, всякими различными фоновыми процессами сложного мерджа. А, но это усложняет код не только, а, с точки зрения реализации вот этих мерджей, но теперь у нас все ещё чтения будут довольно долгими и дорогими, потому что мы не можем просто взять и прочитать данные. Нам нужно прочитать отдельно данные, отдельно вот эти дельтаизменения, помёрзнуть их и только потом там исполнять последующий запрос. А, то есть это по факту не проблема, это сложность, с которой придётся столкнуться. А следующий факт - это то, что OLTP и системы довольно сильно завязаны часто на сильные, ну, на большие гарантии консистентности. А для OLTP нагрузок норма частые точечные перезаписи а объектов. А значит ээ там конечный клиент, делающий эти запросы, хотел бы видеть систему в некотором консистентном виде. А для этого в SBD используются протоколы Concurrency Control Protocols, которые нацелены на там resolve вот таких вот concurrency проблем. А — control, да. А сбился. В смысле? Одним из наиболее частых таких подходов является реализация MVC, который, а, добавляет к самим значениям данных ещё некоторую метаданную о версиях, э, которая и используется для resolv concurrency там расхождений. А, и теперь в OLAB системе, где данные у нас, короче, данные по колонке разнесены, нам нужно ещё как-то это всё поженить с версиями для конкретных значений. А, и это всё выливается в большое количество там ифов, условий, чтений и мрдри различных версий, что, конечно, реализуемо. всё ещё используется в allстемах, но это даёт существенный вклад э в время исполнения запроса, и поэтому вы
Segment 8 (35:00 - 40:00)
уже заведомо не сможете посоревноваться в производительности там с изначально OLTP системой. А, ну и следующая проблема - это сложность поддержки индексов. А точечные чтения подразумевают то, что мы хотим довольно быстро прочитать конкретную запись. Для этого используется, как уже рассказывалось, B+ деревья. А, но в случае хранения колонок по отдельно, ну, короче, в случае отдельного хранения колонок такое поддерживать сложно. И для all нагрузок это в целом и не нужно. Нам хватает там упомянутых ранее подходовта скиппинга. А, поэтому, как правило, вла системах на это закрывают глаза и не реализуют. А перейдём теперь к бенчмаркам. Мы обсудили там разные подходы и идеи того, как можно ускорить, э, аналитику и хранение данных в рамках нашей СБД. Но хочется увидеть какие-то реальные цифры, насколько это действительно помогает. А прежде всего хочется сказать, что бенчмарки обладают своим рядом проблем, а первая из которых - это нерепрезентативность, а сложно найти бенчмарк, который будет, ну там соответствовать в вашей действительной нагрузке системы, под которую вы хотите подобрать себе СУБД. А поэтому часто довольно часто это какие-то искусственные наборы записей, наборы запросов, наборы датасетов. э без отсутствия реальных там перекосов данных, которые возникают в стандартных продовых системах. А следующей проблемой является то, что бенчмарки редко покрывают э там продовый ворклод, а с убд, где у вас помимо ваших запросов чтения параллельно льются ещё разные пишущие запросы и запросы обновления записей. А следующий факт, то что бенчмарки скрывают компромиссы, которые принимались разработчиками на этапе построения и дизайна конкретной СУБД. Там, например, какие-то системы были реализованы там о очень реализованы под flerance какие-то под там более а оптимальный мрgeж фоновых дельта изменений. И, к сожалению, все бенчмарки скрывают вот эти вот компромиссы, пытаясь выдать там оценку, а, как правило, может, там одной чиселкой или двумя, а, и потом проранжировать системы, которые прогонялись через этот бенчмарк. А, ну и последняя проблема, что бенчмарки очень чувствительны к конфигурациям. Довольно часто можно видеть разницу в бенчмарках, там при запуске одного и того же бенчмарка поверх одной и той же системы, но в зависимости там от разного процессора, памяти и всего прочего. И также часто под бенчмарки дополнительно тюнят систему, там подкрутить какие-то коэффициенты, чтобы запросы конкретного бенчмарка проходили чуть лучше. А тем не менее OL системы бенчмаркаются и бенчмаркаются активно. и этим сравниваются, меряются и выделяют следующие три популярных там бенчмарка для улаб нагрузок. Первый из которых - это ClickBench, а это бенчмарк от Кликхауса. А он довольно легковесный, он публичный. Там небольшой датасет, это одна табличка, а 43 простых запроса поверх одной таблички. И это такой, а, гонка на скорость холодного старта в мире аналитических систем, поскольку он меряет то, как ваша система оптимально работает с диском, насколько хорошие и как много статистик она хранит, и насколько там хорошо у вас реализован движок исполнения запросов. А следующий такой уже более крутой бенчмарк - это TPCh. А этот и последующий бенчмарк - это там бенчмарки такие более официальные, которые требуют аудита. и проводится консорциумом TCP. А, ой, я опечатался. TPC TCP и в двух местах. — По-разному немножко, да, но ничего страшного. — TPC вот, вот правильно. TPCH - это такой классический all нагрузка. У нас уже там не одна таблица, а восемь. А мы можем варьировать масштаб ээ там входных данных от гигабайта до терабайта. И, как я сказал, у этого бенчмарка довольно сложные критерии прохождения. А этот бенчмарк уже включает в себя джоины, поэтому он уже проверяет чуть более advanced вещи в движке вашего исполнения, а, в частности там оптимизаторы э
Segment 9 (40:00 - 45:00)
запросов, там как вы упорядочили порядок дерева джоинов и так далее. А, ну и последний такой, самый жёсткий бенчмарк - это TPCDS. А он уже включает в себя сотни сложных SQL запросов с там алапкубами, оконными функциями, ЦТшками и так далее. А тут уже довольно такой серьёзный датасет с перекосами, а, с перекосами данных, с ещё сценарий используется для с параллельной вставкой и обновлениями, но он, в свою очередь, является самым репрезентативным и довольно сложным. А этот бенчмарк интересен ещё тем, что в рамках его системы меряются не только тем, насколько они быстро или медленно его проходят, а даже сам факт того, что ваша OLAB система способна пройти TPCDS - это довольно круто, потому что а вот в числе этих сложных запросов есть запросы, которые могут просто там физически в какой-то момент не влазить в память там разумной конфигурации системы. и вы будете падать по ООМУ. А, но мы сейчас рассмотрим более подробно именно ClickBНch. А, как я упомянул, это одна таблица, а, 100 млн строчек, 105 колонок, а, примерно 70 Гб сырых данных и 43 агрегирующих запроса. А ClickBch прекрасен тем, что у него есть там публичная веб-страничка, куда вы можете зайти, посмотреть там текущий рейтинг и посмотреть, как запросы исполняются. А по таймингам, в частности, там вы можете увидеть там вот такой светофор, где для каждого запроса написано время исполнения той или иной системой в конкретной конфигурации, а и там зелёным цветом обозначены запросы, которые в Короче, если вы выбираете несколько систем, зелёным, обозначено там baseline, то есть самый быстрый запрос, и в сторону уже красного идёт ух ухудшение. А, в частности, в рамках этого бенчмарка смотрится ещё, а как много дискового пространства занимает система для хранения той таблички единственной, а, и время старта системы для того, чтобы начать исполнять запросы, поскольку это для кого-то может быть тоже важной характеристикой. А сперва мы возьмём такую систему, как Mar DB, и рассмотрим её под призмой кликбенча. А интересно рассмотреть эту систему. Изначально скажу, что Мария DB - это fork MySQL, а который был направлен на то, чтобы там поправить какие-то проблемы MySQэля, по факту, э, стал чем-то вообще другим. А изначально, э, поскольку это Fork MySQL, это было типичный allup движок. ой OLTP движок с NSM Storage Model, а, и итеративным Executionм, а, но интересен он для рассмотрения нам, а, по причине, что относительно недавно туда был добавлен Colum Store. Это отдельный типа поддвижок системы, позволяющий обрабатывать аналитическую нагрузку. А, и он уже, в свою очередь, имплементирует DSM и векторизованный движок исполнения. И если мы там посмотрим, цифры, вряд ли будет видно, но тут скорее акцент именно на том, что стандартная там строчное хранение и итеративный движок - это прямо сильно медленнее для аналитических запросов, чем а хранение колонок и векторизованный движок. — Кто бы мог подумать, да? — Да, удивительно. — Дадада. — Разница в запросах тут может достигать нескольких тысяч. Там, например, а первый у Так, это не перелист. Вот первые пару запросов в случае column stстора исполняются там вообще за сотни миллисекунд. В случае, когда в обычном maria dB - это порядка часа. А, но также интересно тут то, что, а, размер данных хранения NSM и уже DSM отличается в среднем в четыре раза. То есть тот самый датасет примерно в 70 Гб сырых данных в случае колоночного хранения превращается в 18, а в случае там обычного хранения ээ построчного это, ну, примерно те же самые 80 ГБ, 8070. — Ну, это из-за внутреннего сжатия, собственно. — Ну да, это вот те самые кодыки сжатия данных. А, но рассмотрим чуть более интересный пример для кликбенча. А на основе системы разработки, которой занимаюсь я с коллегами. А и интересна она в том, что мы используемck
Segment 10 (45:00 - 50:00)
исключительно как Execution Engine. То есть клиckу - это там быстрый векторизованный, имеет быстрый векторизованный движок исполнения запросов, а Yurus в отдельности реализует там свой слой хранения, предоставляющий возможность хранить ваши таблички в формате, который вы захотите. Это может быть либо NSM, либо PAX. А в нашем случае NSM - это называется lookup формат, а PX - это сканформат. Ну, соответственно, там Look под точечные доступы к кортежам, а PКС для аналитики, для сканов. А тут тоже в целом ожидаемо, что строчное хранение будет медленнее, но теперьто мы использовали там чуть более быстрый движок векторизованный. Мы сразу несколько можем операций выполнять, но это быстрее, чем э итеративный движок мb. Э, но всё ещё там порядка 40 раз. Это для первых запросов медленнее. А по стореджу занимаемому мы имеем всё ту же картину. Это там порядка 18-19 ГБ для а скан таблицы, то есть пак. Итаблица - это 70 ГБ. А интересно тут взглянуть именно на хвост. Сейчас мы глянем подробнее на парочку запросов, но сразу заметим, что так вот, что хвост в целом в абсолютных значениях различается не сильно. То есть что колоночное, что построчное хранение, — ну там на порядок всего-то. — Ну да, — да. А даже не порядок, там два раза там 200 мисекунд против там, — ну три раза. Три раза, да. — Да. Два-три раза. А рассмотрим первый запрос. Это там он маркируется как Q1 в табличке. А это довольно простой запрос. Мы хотим пофильтровать там по колонке, она не является ключевой. А и посчитать количество строчек, которые удовлетворяют этому предикату. А в случае пак мы в случае колонночного хранения мы выполняем этот запрос довольно быстро, а поскольку мы используем Minx статистики для того, чтобы сразу же отсечь ненужные скан объекты, а потом, а, короче, в векторизованных движках и в кликхаусе, в частности, а, используются там оптимизации для того, чтобы, а, количество строчек быстрее считать без фактического скана. Но при NСМ нам всё ещё необходимо будет, а, вычитать сильно больше лишних данных и колонок. И следующий запрос - это вот как раз те хвостовые, которые абсолютно отличаются не сильно. А тут у нас там запрос немножко упрощён. Это там, если вы возьмёте и запустите, не выполнится такой у вас SQL. Но по факту запрос представляет из себя предикат поверх колонок, ключевых колонок. то есть колонок, которые праймарики являются, а-а, с последующей группировкой, а, и сортировкой. И в данном случае обе оба, оба формата хранения справляются с этим в целом одинаково. И всё это происходит из-за того, что, а, предикат, а, обладает сильной селективностью, и мы скипаем очень большую часть данных, а, и выполняем запрос там над небольшим датасетом — в памяти. — В памяти уже, да. А таким образом, в контексте Читя, а PКС быстрее, чем, то есть колоночное хранение быстрее срочного примерно в 7-74 раза. Эта цифра это тоже показатель, который есть прямо на кликбче, а который вычисляется в зависимости там от вот набора вот этих значений за 43 запроса. А, и компактнее в случае, опять-таки йтизауруса в три ишесть раза, но данный показатель сильно зависит от кодека, реализованного в системе. Те же самые алгоритмы LE и всё, ну, как я сказал, зависят от эвристик. И то, как вы реализуете эвристики в вашей системе, как вы будете их определять и там компоновать с друг другом, сильно сказывается на итоговом результате. А, и скриншот, который был там на начале кликбенча в качестве его интродса, это там набор показателей каких-то топовых систем из кликбнча. В частности, это кер DB, сам Click Houseу и Single Store. А это довольно интересные сами по себе системы для изучения, а, и ознакомления с ними. А KERDB - это prodдакшн, а, реализация такого resarchш-проекта, как, а, umbra, а, который довольно детально описан там
Segment 11 (50:00 - 55:00)
в пейперах. И, короче, интересно изучить, из чего состоит ээ там самый быстрый на текущий момент в кликбенче движок. А сам click, а single store - это subD, которая позиционирует себя как Habстема для всех ваших типов нагрузки. Но видно, что тут она там вот, ну, вот тех самых запросах, которые в случае оп, — казалось бы, простых тех самых, да, — простых бывает дольше. А на этом у меня всё. Спасибо за внимание. — Спасибо тебе, Влад. Очень познавательный, плотный доклад. А теперь ваши вопросы. Мы ждём их в чатике. Так, первый вопрос у нас там поступил заранее. Наши отношение как роджб. Что мы скажем? Ну вот мы так в куларах это обменялись. Ну не особо, наверное, у нас есть какое-то отношение к Рождб. Вот поэтому, ну вот как-то нет, да. А что ещё? Так, от себя вот спрошу, э, что такое вот есть слово для колоночных баз. Проекция — м — всё время фигурирует. Я думаю, стоит пояснить. — А, презентация ушла. А, ну вот я могу долиставать в целом. Вот на этапе, когда мы рассматривали различные подходы а сжатия наших данных, в частности le, мы упоминали, что, а, хотелось бы, чтобы эта колонка была сортированной, потому что мы и сожмём лучше и компактнее всё будет. Но, в частности, это же может система, ну, короче, схема данной таблицы может не подразумевать сортировку. этой колонки. А так вот сама система может типа искусственно добавить а хранение как бы отсортированной колонки. Это будет какая-то проекция ваших исходных данных а в другой вид и чуть более эффективно исполнять запросы поверх таких колонок в последующем. В частности, опять долго листать. Если мы вот взглянем на, где я уже пролистал, вот на кедрb можно увидеть, что, несмотря на то, что они хранят колонки, а в сравнении с тем же самым Клинкхаусом и синглстором, у них в два раза больше хранимых данных. А что говорит о том, что они почти, наверное, хранят какие-то разные статистики и проекции одних и тех же данных для того, чтобы вот все остальные запросы потом выполнять быстрее всех. То есть это, резюмируя, это какой-то там изменённый вид ваших данных, которые система в зависимости там от своих эвристик и статистик. То есть проекты ещё могут строиться даже рантайм, то есть там если система выполнила какой-то запрос, довольно часто поняла, что вот, блин, этой колонки часто обращаются к сортированной, давай-ка я отсортирую её и положу рядом для того, чтобы в последующем использовать её в сортированном виде. То есть, короче, вот я так объясняю обычно, что такое проекция. Проекция - это индекс для колонок. — Ну, какой-то вторичный, конечно, вторичный, да, конечно, вторичный. Вот я бы так сказал. И современные Аabси системы умеют строить проекции сами без того, чтобы явно их создавать. Это очень важная, кстати, особенность, — да. Проекции можно ещё и создать. — Можно создать самому, да, как индексы. А вот может ээ колонно колоночная на основе статистики проекции делать под капотом для ускорения запросов внутренняя оптимизация такая. — Такие вот умные движки у колоночных баз. Так, следующий вопрос. Можете назвать самые актуальные А нет, сначала зачитаю мнение. Очень классный доклад. Очень интересно было послушать. Это мнение Данила. Самый актуальный алап на текущий момент. Какие у нас? Те, что вы уже показывали по банчам. Ну вот я упоминал, что кердб довольно интересная система в с точки зрения изучения её как там научных работ, которые они выпускают. У них там куча статей. Это какой-то там немецкий университет. Сейчас ходу не возьму вспомнить. А вот а ещё из интересных YDB двигаются в сторону там, а costbas based оптимайзера. И тоже очень прикольно наблюдать за их выступлениями и докладами. Прошлый докладчик также говорил, что очень много интересных докладов у них. А dayклик House как в целом отечественный продукт и система с открытым исходным кодом, в который вы можете залезть, посмотреть, поизучать. Но бытует мнение, что это там система для одной таблички
Segment 12 (55:00 - 60:00)
а что там TCPDS, оно не про TPCDS. Опять я оговорился, типа CDS она не проходит, э, на некоторых запросах уомится, но это всё ещё интересная система. — Я от себя скажу, как практикующий инженер, который именно находится на стороне аппликейшена, да, не базы данных аппликейшна, на самом деле клиckхау топ- один. Вот серьёзно. То есть, если вы се на данный момент в России, да и не только, на самом деле, в России клиckхаус, наверное, это самая популярная, самая удобная алапсистема, поверх которой строятся девечи, даталейки огром совершенно разных уровней корпораций, не каких-то маленьких проектиков, где у вас десятки, сотни терабайт данных, даже петабайты. Кликхаус имеет много там нативных коннекторов реализованных, которые позволяют вам там сразу читать и скавки и вставлять там в материализованное представление поверх этого, там агрегаты считать сразу же. То есть можно, да, реализовывать сложные схемы для ваших ДВХ, там BI и прочего. — А можно тезисно про отличия с точки зрения сценариев использования между Клихаус и Кассандра? Кассандра упомянули. затрудняюсь дать ответ. У меня такое как, да, как у разработчика, который часто работает с одной системой, однобокое представление. — Ладно. Не очень понимаю, в какой момент нужно вообще хотеть проводить бенчмарки БД в реальных проектах при выборе базы, для тестирования конфигурации сервер, для получения пиковых возможных параметров текущей базы. — А — сложный какой-то вопрос. Давай мы его скипнем. — Для чего бентичмарки проводить? Наверное, — так. Это общий вопрос, на который может долгий ответ давать. Там вы можете под конкретный сценарий ваш бенчмаркет систему. А, например, там это не использование каких-то готовых бенчмарков, которые в том числе были упомянуты, а, а там вы просто скрафтили свою там простую среду со своими нагрузками и просто для себя посмотрели, какая система лучше справляется с вашим именно сценарием использования. Насколько различные лапс УБД - это решение из коробки? Хороший вопрос. Если выполнение сильно зависит от эвристик, выборов алгоритмов, конфигурации, насколько движок сам этим может рулить, а насколько требуют ручной настройки? — Ну прикольный вопрос. Это всё-таки, мне кажется, не з не думать об этом - это не обязанность уже пользователя СБД. Это там разработчики заранее стараются продумать всё и реализовать, чтобы вы в дальнейшем пользовались. Но в том числе это на это можно влиять. А и это уже, мне кажется, advanced штуки, э, которые подразумевают своё наличие в коро в коробочном решении. Но, как я сказал, это всё ещё коробочно. То есть вряд ли вам понадобится когда-то повлиять на то, там, а когда выбирается конкретный алгоритм сжатия. Вы, конечно, можете задать это, но а — я, наверное, тоже от себя как практикующий инженер скажу, что очень редко. То есть вот лезть бей, лезть внутрь алап системы надо очень редко, как правило. Вот. Но бывает такое не бывает. Ну, это гораздо реже, чем в LTP системах. Вот в LTP системах там прямо вот надо лазить, — да, там индексы вот эти все самому настраивать, — да, без этого. Ну, про это вот целый доклад был. Без этого никак вообще. Это как бы альфа и омега — для LTP. Давай, последний вопрос. Насколько я знаю, Col column storage index MySQL - это как раз сжатая таблица с помощью подобных алгоритмов. То есть инструменты, ведь она уже есть в LTP. Как вы считаете, как понять, что пора делать реплику данных в Аab из OLTP? Короче говоря, ну ты про это говорил, что это путь к гибридным системам, — да, — собственно, это вот оно. Это то — там тоже мир кучи компромиссов и различных подходов. То есть прямо идеального, а гибридного хранения, как мне кажется, сложно достичь. вы всё ещё там будете одну из частей делать медленнее. — Но действительно ответить, наверное, на вопрос, когда задуматься ОTP гибри мигрировать в All, то это, мне кажется, зависит напрямую от вашего сценария использования, — от бизнес-задачи. Короче говоря, я вот завершу, наверное, таким такой репликой, что вот на последнем питерском хайлоуде выступал Вова Комаров, и у него был как раз рассказ про как ну эволюцию баз данных там, в принципе, да. И вот у него
Segment 13 (60:00 - 60:00)
был такой интересный вывод в конце, что, ну, мы все движемся втопсистемы, да, гибридные, и что типа вообще-то классная Псистема - это White is our в порядке рекламы, это вот такое мнение, с которым я согласен. Спасибо.