Этой статьей я открываю серию материалов про инфраструктуру для аналитики вообще и экзотическую для России базу данных Vertica в частности. Статьи описывают опыт серии проектов в моей компании LifeStreet и не претендуют на полноту. Однако, где это представляется возможным, я буду пытаться давать общие обзоры. Прежде чем начать разговор собственно о Вертике, я хочу рассказать немного о том, как мы к ней пришли. Начнем с истории развития аналитической инфраструктуры в нашей компании.
Часть 1. Немного истории, теории и практики
Традиционно мы исповедуем итеративный процесс разработки всего нового. То есть сначала делается быстрый прототип, чтобы “пощупать” некоторую предметную или технологическую область. Затем, отталкиваясь от прототипа, разрабатывается архитектура и дизайн “как надо”, причем предпочтение отдается быстрым в реализации достаточно хорошим решениям, нежели академически правильным, но долгим и сложным. Затем, понятие о том, “как надо”, меняется, и архитектура модифицируется, “как на самом деле надо”. И так далее. Все изменения происходят на работающем и динамично развивающемся бизнесе, что требует осторожного эволюционного подхода. Так было и с аналитической платформой.
Первая версия “инфраструктуры” была сделана “на коленке” за два дня в далеком 2006 году, когда в компании было 4 человека разработчиков, и примерно столько же людей из бизнеса. Это был MySQL с одной таблицей и один Java-класс, который обрабатывал и загружал логи nginx. Даже такого минимального набора уже было достаточно, чтобы начать делать некоторые маркетинговые эксперименты и зарабатывать деньги. Затем был продолжительный период дизайна и уточнения требований, в результате которого были выработаны модель данных, структура фактов, измерений, метрик, агрегатов и т.д,. Все это согласовывалась с параллельно разрабатываемой онтологией рекламных интернет-компаний. В течение нескольких месяцев на том же MySQL была построена первая версия нашего аналитического хранилища данных, включающая в себя все основные компоненты: физическую и логическую схему, ETL, написанный частично на хранимых процедурах, а частично на джаве. Позднее в качестве клиентской части мы стали использовать MicroStrategy, от которой потом отказались в пользу своей собственной разработки. Забегая вперед, отмечу, что эта инфраструктура с незначительными изменениями проработала 4 года, и на ней наша компания стала стремительно завоевывать рынки.
Однако, мы понимали, что решение на MySQL временное, так как бизнес требовал масштабируемой инфраструктуры, которая могла бы поддерживать до миллиарда фактов в день. Оптимистичные оценки обещали до 400 терабайт сырых данных в год. Тогда мы приняли вполне разумное, но неправильное решение идти на Oracle. За год мы построили хранилище данных на Оракле на правильном железе и софте в соответствии со стандартными оракловскими практиками вроде materialized views, и оно даже работало, и достаточно быстро. Помимо скорости самого Оракла, мы смогли горизонтально масштабировать ETL. Если бы не одно “но”. Оракл совершенно невозможно было поддерживать в рамках тех процессов, который протекали в компании. А процессы простые — постоянное инкрементальное развитие, добавление новых полей, сущностей и т.д. Любое новое поле, которое даже косвенно затрагивало MV, вызывало каскад перестроек, полностью блокирующий систему. Попытки это побороть ни к чему не привели. В конце концов мы отказались от Оракла и решили еще немного потерпеть с MySQL, пока не найдем вариант получше. Очень кстати в это время мы наткнулись на компанию Tokutek и ее продукт TokuDB.
Tokutek, расположенный в пригороде Бостона, — это одна из многих компаний, которые на базе MySQL делают что-то хорошее. TokuDB — это storage engine для MySQL, обладающий несколькими уникальными и очень полезными для аналитических приложений свойствами. Наверное, в этом месте стоит отступить чуть в сторону, и рассказать об особенностях дизайна схемы для аналитических приложений.
Традиционный подход к дизайну схемы баз данных для аналитики — это так называемая стар-схема или звездочка. Смысл в том, что есть большие центральные таблицы фактов и много относительно небольших таблиц измерений (dimensions). Таблица фактов содержит метрики и ключи измерений. Если нарисовать таблицы и связи в каком-нибудь редакторе или просто на доске, то получится звездочка или солнышко, где в центре таблица фактов, в на лучиках таблицы измерений. Отсюда и название. В таблицах измерений данные часто денормализованны и организованы в иерархии. Хрестоматийные примеры — иерархии времени и географии. Например, такая таблица:
dim_geo
- geo_key
- city
- state
- country
С точки зрения нормализации данных — это плохо, так как одни и те же state и country повторяются много раз. Но с точки зрения аналитической модели — это, наоборот, удобно и эффективно, так как не надо делать лишних джойнов.
Стар-схема и иерархии диктуют типы типичных запросов. Например, пусть мы хотим получить статистику по количеству показов нашей рекламы в разных странах.
select date(event_date), sum(impressions) from fact_table join dim_geo using (geo_key) where country = ‘RU’ group by 1;
Что существенного в этом запросе? Те, кто умеют читать планы выполнения, сразу же догадаются, что в данном случае будет большой index range scan по geo_key для России, если в fact_table есть индекс по geo_key, и full scan, если его нет. Потом сортировка по date(event_date). Если в таблице сотни миллионов или миллиарды строк, то такой запрос вряд ли будет быстрым на MySQL.
Даже из этого простого примера можно предположить, что во-первых, аналитические запросы “любят” разные индексы. Во-вторых, эти индексы разряженные, то есть одному уникальному значению индекса соответствует много записей. И третье, что агрегирование и сортировка большого количества строк — это довольно типичная операция. Об этом мы еще поговорим потом, а сейчас вернемся к возможностям TokuDB, и как они помогают в аналитических приложениях.
Самое важное — это собственная патентованная технология индексов, использующих не бинарные, а фрактальные деревья. К фракталам это отношения никакого не имеет, просто слово красивое, и как они работают в деталях, я не буду объяснять — сложно. (Хотя, если интересно, могу написать отдельную статью или отослать в старую статью на mysqlperformanceblog, где есть ссылки на академические материалы). Ключевое отличие от бинарных деревьев с точки зрения пользователя — это производительность вставок и удалений при больших размерах таблиц. Те, кто серьезно занимаются базами данных, знают, что в том же MySQL при размере таблицы в сотни миллионов строк и больше, добавление новых записей может занимать существенное время, особенно, если в таблице несколько индексов, которые не влезают в индексный кэш. Происходит это из-за того, что во-первых, для того, чтобы “пройти” по индексу, приходится читать его кусочками с разных мест диска. А во-вторых, добавление нового “листа” может вызвать (и вызывает) перебалансировку индекса, которая приводит к перемещению или замещению значительных частей индекса. В общем, серьезная проблема. Во фрактальных индексах TokDB она решена, и производительность вставок и удалений практически не падает с размером таблицы и с количеством индексов в ней. В частности, это достигается тем, что весь путь по дереву индекса “укладывается” на диск так, чтобы его можно было прочитать последовательно. Плюс хитрый кэш.
Второе серьезное преимущество TokuDB — это clustering индексы. Суть в том, что вместе с индексом хранится вся запись. Это существенно влияет на скорость запросов, особенно для запросов по разряженным индексам, так как в противном случае по индексу сначала получаются ссылки на данные, и только потом читаются сами данные. В MySQL для MyISAM индекс и данные всегда хранятся отдельно, а для InnoDB данные хранятся отсортированные по первичному ключу, а остальные индексы отдельно. Для аналитических задач одного индекса как правило мало.
Эти два конкурентных преимущества привели к тому, что мы выбросили Оракл и прожили на MySQL с TokuDB еще 3 года. Их продукт постоянно развивался, они с готовностью оказывали поддержку, несмотря на то, что мы пользовались бесплатной лицензией. Уже когда мы почти перешли на Вертику, в TokuDB появились две еще более “сладкие” фичи: “горячее” добавление индекса, и “горячее” изменение таблиц. В стандартных движках MySQL эти операции блокируют таблицу. Если таблица размером в несколько десятков гигабайт, то надолго.
Итак, наша инфраструктура на конец 2009 г — это MySQL с TokuDB, в который загружается примерно 150-200 миллионов фактов в день. Факты хранятся недолго, но агрегируются при загрузке в десятка два агрегатов разной гранулярности. Некоторые агрегаты “вечные”, другие хранят данные лишь несколько дней или недель. Можно и дольше, но мы ограничены бесплатной лицензией на TokuDB, поэтому размер агрегатов не превышает 50GB. У нас несколько более-менее идентичных систем для резерва или под специфические задачи. Основной клиентский интерфейс — MicroStrategy, но мы уже начали процесс перехода на свое решение, разработав первые версии универсального сервиса для выполнения аналитических запросов на новом для нас языке Scala. У нас есть проблемы и с производительностью, и с масштабируемостью, которые пока не бьют, но уже кусаются. Мы стали настоящими профессионалами в настройке MySQL и проектировании приложений для него «правильным» образом. Но этого не достаточно.
Часть 2. Выбор специализированной аналитической базы данных
TokuDB позволил нам довольно спокойно провести внутренний конкурс на самую лучшую специализированную базу данных для аналитики. Для того, чтобы понять, что особенного может быть в такой базе данных, вернемся к примеру со статистикой по странам из первой части.
Представьте себе, что в таблице ‘fact_table’ 100 колонок. Это вполне реалистичная цифра, у нас сейчас, например, больше. Запрос использует всего три колонки — 'event_date', 'impressions' и 'geo_key', — но в традиционной базе данных данные хранятся по строкам. Вне зависимости от того, сколько колонок нужно для выполнения запроса, прочитаны с диска будут все, что очень неэффективно для таблиц, где колонок много. Этот неприятный эффект заметили довольно давно, и первая база данных, которая использовала колонко-ориентированную (column-oriented) внутреннюю структуру хранения данных был побочный продукт Sybase SybaseIQ, разработанный в середине 90х. Насколько я понимаю, он не получил должного признания. Но в середине 2000х к этому вопросу вернулись опять, и пионером выступил небезызвестный Michael Stonebraker с исследовательским проектом C-Store, который впоследствии стал основой для комерческой платформы Vertica. О C-Store я узнал в середине 2008г из переводной статьи на CITForum, и это стало поворотным моментом.
Преимущества колонко-ориентированного хранения не исчерпываются лишь тем, что достаточно читать с диска только необходимые для запроса колонки. Данные в колонках обычно очень хорошо сжимаются. Например, если у вас в колонке всего два значения, то можно закодировать их битами, если несколько — то словарем. Если значения группируются вокруг какого-то центра, то можно кодировать дельтами. И так далее, вариантов много. Все это позволяет в разы или даже десятки раз сжимать данные, существенно уменьшая объем дисковых операций. Другое очевидное свойство состоит в том, что колонки очень просто добавлять, это практически бесплатно. Есть и другие, менее очевидные. Но все эти свойства объединяет одно: они очень подходят для баз данных, используемых для аналитических задач и типов запросов.
На конец 2009г было всего несколько коммерческих баз данных, которые предлагали колонко-ориентированное хранение данных:
- Vertica
- GreenPlum
- InfoBright
- ParAccel
- InfiniDB
- Oracle (Exadata)
Они были на разной степени готовности и доступности, но мы их попробовали все, кроме Оракла. Для тестирования мы подготовили таблицу, в которой было 25 колонок и 12 миллиардов строк. Восемь тестовых запросов измеряли разные аспекты производительности; во всех из них было суммирование и фильтрация, в некоторых — группировка и подзапросы. В качестве референсной системы выступал MySQL c TokuDB. В тех случаях, когда база данных позволяла масштабирование путем добавления серверов в кластер, мы тестировали, насколько это улучшает производительность.
Проект длился около двух месяцев силами одного человека, так как каждая из систем требовала некоторого изучения, настройки и загрузки данных. Результаты всех экспериментов сводились в общую таблицу. В результате Вертика обогнала всех. Односерверная система работала в 20-100 раз быстрее референсного MySQL, и в 2-4 раза быстрее Greenplum, который занял почетное второе место. Вертика масштабировалась линейно, в отличие от GreenPlum, где добавление второго сервера увеличило производительность лишь на половину. Причем, как мы потом поняли, это был не предел, и при более оптимальном физическом дизайне Вертика могла быть еще быстрее. Таким образом, выбор был для очевиден, осталось лишь подписать выгодный контракт и начать мигрировать нашу инфраструктуру на новую базу данных.
Об этом и об особенностях Вертики в следующей статье.
Автор: alexzaitsev