Exasol — это современная высокопроизводительная проприетарная СУБД для аналитики. Ее прямые конкуренты: HP Vertica, Teradata, Redshift, BigQuery. Они широко освещены в Рунете и на Хабре, в то время как про Exasol на русском языке нет почти ни слова. Нам бы хотелось исправить эту ситуацию и поделиться опытом практического использования СУБД в компании Badoo.

Exasol базируется на трех основных концепциях:

1. Массивно-параллельная архитектура (англ. massive parallel processing, MPP)


SQL-запросы выполняются параллельно на всех нодах, максимально используя все доступные ресурсы: ядра процессоров, память, диски, сеть. Понятие «мастер ноды» отсутствует — все серверы в системе равнозначны.

Отдельные стадии выполнения одного запроса также могут идти параллельно. При этом частично рассчитанные результаты передаются в следующую стадию, не дожидаясь окончания предыдущей.

2. Колоночное хранение (англ. columnar store)


Exasol хранит данные в колоночной форме, а не в форме отдельных рядов, как в классических СУБД. Каждая колонка хранится отдельно, разделяется на большие блоки, сортирируется, сжимается и равномерно распределяется по всем нодам.


Эффективность сжатия сильно варьируется в зависимости от типов данных и распределения значений в таблицах. В среднем данные в Badoo сжаты в 6,76 раз.

Колоночное хранение позволяет многократно ускорять аналитические запросы, а также вычитывать только те данные, которые необходимы для выполнения запроса. В классических СУБД необходимо прочитать весь ряд целиком, даже если в нем используется всего одна колонка.

3. In-memory analytics


В Exasol есть механизм, похожий на buffer pool в MySQL или shared buffer в PostgreSQL. Блоки данных, однажды загруженные с диска, остаются в памяти и могут быть повторно использованы для последующих запросов.

Как правило, в реальной жизни пользователи работают в первую очередь с «горячими» данными (последний день, неделя, месяц). Если у кластера достаточно памяти, чтобы вместить их целиком, то Exasol не будет трогать диск вообще.

Эти три концепции, собранные вместе в одной СУБД, показывают высокую производительность относительно сложности SQL-запросов и используемого «железа». Приведем конкретные цифры.

Сейчас в кластере Badoo используется 8 серверов со следующими характеристиками:
  • от 16 до 20 CPU Cores;
  • 768 Гбайт RAM;
  • 16x1 Тбайт HDD (RAID 1 — 8 Тбайт);
  • 10 Гбит сеть.

Общий объем памяти, доступной Exasol, составляет приблизительно 5,6 Тбайт.
Общий объем данных без сжатия — около 85 Тбайт.

Размеры крупных таблиц варьируются от 500 миллионов до 50 миллиардов рядов. Один аналитический запрос обрабатывает в среднем около 4,5 миллиардов рядов.

Производительность на реальных запросах за последний месяц:
Кол-во объектов в запросе Все запросы Запросы длительностью от 1 секунды
Кол-во Медиана Среднее Кол-во Медиана Среднее
До 3 240914 0.021 сек 9 сек 34808 29 сек 63 сек
От 4 до 10 45122 13 сек 47 сек 30005 34 сек 70 сек
От 11 до 30 12642 24 сек 69 сек 5615 45 сек 156 сек
От 31 и больше 740 41 сек 303 сек 740 41 сек 303 сек

Это не синтетический тест, а реальная статистика по настоящим запросам живых пользователей. Как можно увидеть, даже в самых сложных случаях, речь чаще всего идет о секундах и минутах, но никак не о часах или днях.

Сложность запроса в этом примере условно рассчитывается путем простого поиска слов FROM и JOIN в тексте SQL. Таким образом, мы находим примерное количество использованных объектов. Чем больше объектов используется, тем сложнее запрос.

Выделение в отдельную группу запросов длительностью от 1 секунды необходимо для того, чтобы снизить влияние слишком быстрых in-memory запросов на результат и показать наиболее близкую к реальности картину для тех случаев, когда все же нужно что-то прочитать с диска.

За счет чего достигается высокая производительность, помимо перечисленных выше концепций.

Джойны (англ. join) и индексы


Exasol умеет делать очень быстрые джойны, причем совершенно не смущается от их количества.

Эффективный джойн всегда происходит по индексу. Индексы создаются автоматически в тот момент, когда вы впервые пытаетесь объединить две таблицы по определенным ключам. Если индекс не используется долгое время, то он так же автоматически удаляется.

При добавлении или удалении данных в таблицах с уже существующими индексами создаются «дельты», которые содержат только изменения. Если изменений накапливается слишком много (около 20% от общего объема), то происходит INDEX MERGE, который объединяет основной индекс с дельтой, после чего дельта удаляется. Это намного быстрее, чем полное пересоздание индекса с нуля. Механизм чем-то похож на Sphinx.

Индексы занимают относительно небольшой объем памяти. Подавляющее большинство индексов в нашей базе занимает меньше 100 Гбайт, в том числе это справедливо для многомиллиардных таблиц. Размер индекса определяется количеством входящих в него колонок, их типами данных и вариабельностью значений.

Глобальные джойны vs локальные джойны


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


Локальный джойн происходит тогда, когда объединяемые данные хранятся на одной ноде.


Локальные джойны намного быстрее глобальных, потому что в этом случае не используется сеть. Такого эффекта можно добиться, если заранее распределить данные на нодах особым образом. В нашем примере это можно сделать так:
ALTER TABLE CUSTOMER DISTRIBUTE BY CITY_ID;
ALTER TABLE CITIES DISTRIBUTE BY ID;

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

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

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

Условия эффективного джойна


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

1. Условия должны быть только в формате columnA = columnB.

Хорошо:
JOIN table2 b ON (a.id=b.id)

Плохо:
JOIN table2 b ON (a.purchase_date > b.create_date)

2. Нежелательно использовать выражения.

Хорошо:
JOIN table2 b ON (a.purchase_date=b.purchase_date)

Плохо:
JOIN table2 b ON (a.purchase_date=TO_DATE(b.purchase_time))

3. Несколько условий можно объединять при помощи AND, но следует избегать OR.

Хорошо:
JOIN table2 b ON (a.id=b.id AND a.name=b.name)

Плохо:
JOIN table2 b ON (a.id=b.id OR a.name=b.name)

4. Типы данных колонок должны совпадать. DECIMAL к DECIMAL, VARCHAR к VARCHAR, DATE к DATE и так далее.

Если хотя бы одно из условий не выполняется, то происходит следующее:
  • либо индекс все равно будет создан, но после выполнения запроса сразу же удален (так называемый Expression Index);
  • либо Exasol будет делать джойн «всего ко всему», а затем фильтрацию. Последнее не так уж страшно, пока у вас не получается несколько триллионов рядов в промежуточном результате. Миллиарды — вполне возможно.


Root filter


Понятие «Root filter» вносит существенный вклад в высокую производительность Exasol. Его нельзя найти в официальной документации, но можно увидеть в секретных системных «вьюшках» и планах выполнения запросов.

Как работает фильтр?

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

В этих примерах фильтр будет использоваться:
WHERE registration_date BETWEEN '2015-01-01' AND '2015-10-01'
WHERE foo >= 115
WHERE product_type IN ('book','car','doll')

А в этих примерах фильтр использоваться не будет:
WHERE registration_date BETWEEN CURRENT_DATE – INTERVAL '1' YEAR AND CURRENT_DATE
WHERE foo > (15 + 17)
WHERE email REGEXP_LIKE '(?i)@mail\.ru$'

Если Exasol может заранее очертить четкие и простые границы того, какие именно данные ему нужно прочитать, то он воспользуется такой возможностью и в десятки или сотни раз снизит количество операций чтения. Чем меньше чтений и чем меньше размер промежуточных результатов, тем быстрее все работает. При этом никакие специальные индексы не нужны — используется только статистика на базе колоночной структуры хранения данных.

Возможности и особенности SQL


Exasol полностью поддерживает базовый ANSI SQL. Это включает в себя GROUP BY, HAVING, ORDER BY, LIMIT, OFFSET, MERGE, FULL JOIN, DISTINCT и т.д. — словом, все, что мы привыкли видеть.

С точки зрения аналитического SQL, предлагается следующее:
  1. Window-функции, ROW_NUMBER, медианы, ранги, перцентили на любой вкус;
  2. Common Table Expressions;
  3. CUBE, ROLLUP, GROUPING SETS;
  4. GROUP_CONCAT;
  5. регулярные выражения PCRE, включая поиск, захват паттернов и их замену;
  6. функции для работы с датами и таймзонами;
  7. Geospatial-функции.

Нам показалось интересным то, что Exasol воспринимает пустую строку как NULL. Это никак нельзя отключить. Судя по всему, преобразование происходит где-то на очень глубоких уровнях, но серьезных проблем это не создает. Просто нужно об этом помнить при импорте.

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

Транзакции


Exasol — транзакционная СУБД, full ACID compliant. Уровень изоляции — serializable. На практике в текущей реализации это означает, что в таблицу параллельно может писать только один писатель. Читателей может быть сколько угодно, и они не блокируются писателем. «Параллельные писатели» будут выполняться последовательно друг за другом, либо, в случае конфликтов и дедлоков, произойдет ROLLBACK.

DDL-запросы также транзакционны. Это означает, что вы можете создавать новые таблицы, вьюшки, добавлять колонки и т.д. — все это может происходить в одной транзакции. При необходимости можно легко сделать ROLLBACK и вернуть все как было.

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

Импорт данных


Наиболее эффективно импортировать данные в Exasol при помощи встроенной утилиты exajload. Она максимально использует возможности управления потоками и параллельного подключения сразу к нескольким нодам.

Разрешается передавать сжатый поток данных без предварительной декомпрессии, что заметно снижает нагрузку на сеть. Поддерживаемые форматы: gz, zip, bz2.

Для аналитических СУБД актуален вопрос эффективной загрузки данных из Hadoop. Exasol позволяет напрямую загружать файлы в несколько потоков при помощи WebHDFS, минуя промежуточные серверы и снижая общий overhead.

Благодаря транзакционности можно также загружать данные напрямую в production-таблицу, целиком минуя staging. Если загрузка прервется (по любой причине), то произойдет ROLLBACK, и у вас останется старая копия таблицы. Если загрузка успешно завершится, то новые данные заменят старые.

Как и во многих других аналитических СУБД, в Exasol намного эффективнее подход ELT (Extract — Load — Transform), нежели классический ETL (Extract — Transform — Load). Лучше загружать «сырые» данные в промежуточную таблицу, а затем трансформировать их средствами самой СУБД, при этом используя все достоинства Massive Parallel Processing.

Кратко о поддержке


1. В Exasol в настоящий момент нет продвинутой поддержки серверов с разной конфигурацией в рамках одного кластера. Все ноды получают одинаковый объем данных и одинаковый объем вычислительных задач. Весь кластер будет работать со скоростью самой слабой ноды. Поэтому лучше в самом начале поставить 2-4 мощных сервера, чем 10 слабеньких. Расширяться потом вам будет значительно проще.

2. Добавление новой ноды занимает несколько минут. После этого Exasol можно сразу поднимать и пускать пользователей, не дожидаясь окончательного перераспределения данных на дисках. При этом все будет работать чуть медленнее из-за дополнительных сетевых операций, но по мере увеличения количества нод разница становится менее заметной. Процессом реорганизации данных можно управлять вручную, запуская команду REORGANIZE для конкретных схем или таблиц. Таким образом, можно реорганизовать более важные таблицы раньше, чем все остальные.

3. Fault tolerance. В своей практике мы сталкивались с несколькими аварийными сценариями. Например, заканчивалось свободное место, или кто-то случайно физически отключал часть работающих серверов от сети. Никаких существенных проблем с этим мы не заметили. База заранее пишет о проблеме, останавливается и ждет исправления ситуации. В ряде случаев происходит автоматический рестарт, если проблема исчезла. При этом если не заглядывать в логи, можно и не узнать, что вообще что-то происходило.

Exasol способен пережить полную потерю некоторого количества нод без остановки работы. Существует особый параметр «redundancy», который определяет, на сколько нод будет физически сохраняться каждый блок данных. Чем выше значение этого параметра, тем больше нод можно потерять. Но за это ожидаемо приходится платить местом на дисках.

4. SSD диски для Exasol не играют большой роли. Блоки данных очень большие, они читаются и записываются крупными партиями друг за другом. Random disk access практически отсутствует. Вместо SSD лучше поставить больше памяти.

Цены


К сожалению, Exasol — это не open source software. Необходимо оплачивать лицензию. Фиксированных цен нет — с каждым партнером компания договаривается индивидуально. Скорее всего, стоимость будет зависеть от объема используемой оперативной памяти, что типично для СУБД, позиционирующих себя как in-memory.

Также у Exasol есть бесплатный trial. Он представляет собой версию, которая работает только на одной ноде и использует до 10 Гбайт оперативной памяти. Этого достаточно для devel-окружения и для того, чтобы проверить ваши запросы на небольшой части данных и составить общее впечатление.

Кроме того, Exasol запустил специальную программу для стартапов, по которой предлагается 500 Гбайт данных в облаке за 500 евро в месяц. При этом не нужно покупать дорогостоящее железо. К сожалению, мы не можем оценить преимущества их облака, т.к. мы с ним не работали.

Физически компания Exasol находится в городе Нюрнберге (Германия). Можно приехать к ним в гости, пройти обучающие курсы, поговорить с разработчиками.

Заключение


В целом Exasol стал для нас реальным открытием. Вы просто загружаете данные и можете сразу их анализировать на высокой скорости. It just works.

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

Фактически вы ограничены только способностью сформулировать свою задачу в форме SQL-запроса. Если для каких-то особых случаев возможностей SQL окажется недостаточно, то Exasol позволяет создавать custom-функции на Python, LUA, Java, R. При этом сохраняются все плюсы колоночного хранения, общей параллельности всех операций и эффективного использования памяти.

Если вам интересны любые другие аспекты работы с Exasol, а также организация эффективного ETL-процесса — пишите в комментариях, я с удовольствием вам отвечу.

Спасибо за внимание!

Полезные ссылки


  1. Exasol website
  2. User Manual (ver 5.0.11)
  3. Technical Whitepaper — объяснения про Massive Parallel Processing
  4. Free trial
  5. Solution center — в основном полезные видео и ответы на частые вопросы

Комментарии (24)


  1. Keramblock
    26.11.2015 17:05
    +1

    А чем вертика не устроила, если не секрет? Тоже раздумываем над выбором.


    1. alexxz
      26.11.2015 17:20

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


    1. wildraid
      26.11.2015 17:34
      +3

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

      В поисках принципиальных отличий Exasol от Vertica, на последнем Highload++ поговорил с представителями HP и с коллегами из Avito. Узнал следующее:

      1). Vertica всегда читает диск, даже если анализ идёт по «горячим» данным.
      2). Vertica не так эффективно использует большие объёмы памяти. Если я правильно понял ребят из HP, объём памяти на типичной ноде для вертики не превышает 200-300Гб.
      3). В Vertica необходимо строить проекции для того, чтобы делать эффективные локальные join'ы. На больших объёмах нельзя просто придти и начать join'ить какие-то совершенно случайные колонки. В Exasol'е — можно. Индексы построятся сами после первого SELECT'а.

      В целом, исхожу из того, что память быстрее, чем диски. Она становится всё дешевле, её становится всё больше. И чем лучше СУБД работает с памятью, тем лучше для пользователей.

      Но Вертика тоже отличный продукт. Ничего против неё не имею.


      1. boombick
        27.11.2015 02:12

        А вы же, вроде, использовали Vectorwise, если мне память не изменяет. Почему отказались?


        1. wildraid
          27.11.2015 08:28

          Vectorwise — это single-node система. Наступил момент, когда наши объёмы уже нельзя было уместить на одной машине.


      1. alexzaitsev
        01.12.2015 12:54

        Позволю несколько комментариев по поводу Вертики.

        1). Vertica всегда читает диск, даже если анализ идёт по «горячим» данным.

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

        2). Vertica не так эффективно использует большие объёмы памяти. Если я правильно понял ребят из HP, объём памяти на типичной ноде для вертики не превышает 200-300Гб.

        Это тоже не совсем так. Дело не в том, что Вертика не эффективно использует большие объемы памяти. А в том, что память используется в основном во время выполнения запросов. Соответственно, необходимый объем зависит от сложности запросов и количестве клиентов, который одновременно выполняют запросы.

        3). В Vertica необходимо строить проекции для того, чтобы делать эффективные локальные join'ы. На больших объёмах нельзя просто придти и начать join'ить какие-то совершенно случайные колонки. В Exasol'е — можно. Индексы построятся сами после первого SELECT'а.

        И это тоже не совсем правда :) В Вертике можно прийти и джойнить совершенно случайные колонки. В стар-схемах обычно одна таблица очень большая, и много маленьких — этот сценарий работает без проблем. Две большие таблицы могут джойниться небыстро, но как часто в реальной жизни встречаются такие сценарии? Некоторое подобие индекса в Вертике строится в памяти во время выполнения запроса (hash join). Но чтобы получить максимальную производительность (оптимизировать WHERE-clause, group by, джойны) — да, надо делать проекции. Но оно того стоит.


    1. Keramblock
      26.11.2015 17:59

      Спасибо за развернутый ответ)


  1. vdmitriyev
    26.11.2015 21:37

    Спасибо вам громное за отличную статьи. И отдельно спасибо за статистику по нагрузке на реальных данных. Хотелось бы еще обратить внимание на то что у Exasol отличный результаты в «синтетических бенчмарках» аля TPC-H, в чем можно убедиться тут.


  1. Loremaster
    27.11.2015 00:20

    Эх, как жаль, что это не опенсорс! Звучит уж очень вкусно. Спасибо за статью.


  1. alexzaitsev
    01.12.2015 11:54

    Спасибо, давно хотел поподробнее посмотреть на это европейское чудо, и Вы частично удовлетворили любопытство.

    Несколько вопросов по тексту:

    1. «Как правило, в реальной жизни пользователи работают в первую очередь с «горячими» данными (последний день, неделя, месяц). Если у кластера достаточно памяти, чтобы вместить их целиком, то Exasol не будет трогать диск вообще.» — а что происходит с кешем, если происходит постоянная загрузка данных?

    2. «Один аналитический запрос обрабатывает в среднем около 4,5 миллиардов рядов.» — как Вы это определили? Есть подробная статистика?

    3. «Эффективный джойн всегда происходит по индексу. Индексы создаются автоматически в тот момент, когда вы впервые пытаетесь объединить две таблицы по определенным ключам. Если индекс не используется долгое время, то он так же автоматически удаляется. » — то есть базу данных требуется «разогревать», чтобы построить индексы? Это несколько противоречит утвреждению «Вы просто загружаете данные и можете сразу их анализировать на высокой скорости.» Не совсем понятно, кстати, какие это индексы, если JOIN работает только по условию «равно». Бинарные индексы прекрасно подходят и под условия больше-меньше. То есть это хеш-индексы, скорее всего. Оно конечно хорошо, но для джойнов больших таблиц может быть неэффективно.

    4. «Также маленькие таблицы автоматически реплицируются (копируются целиком) на все ноды, что автоматически гарантирует быстрые локальные джойны к ним. Это особенно актуально для многочисленных небольших списков.» — то есть разработчик не имеет над этим контроля? Или все таблицы по дефолту реплицируются везде, и разработчик должнен явно указывать 'distribute by' для больших таблиц? Может ли быть несколько колонок в distribute by? Чем еще управляет разработчик в физическом дизайне?

    Вообще, статья хороша, как вводная, но она плохо объясняет, как именно Exasol достигает высоких результатов. Можно поверить, что он быстро работает, но скорость запросов на ваших дорогущих серверах (768GB RAM) и сравнительно небольших объемах не выглядит большой в сравнении с той же Вертикой.


    1. wildraid
      01.12.2015 13:34

      Класс! Приятно получить комментарий от человека, который в теме.

      1. а что происходит с кешем, если происходит постоянная загрузка данных?

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

      Судя по этим данным, в Badoo ETL процесс практически не вытесняет кеш.
      Вероятно, это связано с двумя самыми популярными стратегиями загрузки данных:

      1. Перезагружаем таблицу целиком каждый день. Это работает для маленьких таблиц до 1 миллиарда рядов.
      2. Загружаем только последнюю неделю\день\час. Это работает для больших и очень больших таблиц.


      В итоге, в кеше оказываются маленькие таблицы целиком и самые новые данные для больших таблиц. А это именно то, над чем аналитики чаще всего проводят анализ.

      2. как Вы это определили? Есть подробная статистика?

      Да, Exasol сохраняет много различной статистики. Он делает скрытый профайлинг всех запросов и хранит очень подробную информацию за последние 24 часа. Буквально хранится каждая стадия выполнения запроса на каждой отдельно взятой ноде. Время от времени он делает агрегацию и сохраняет обобщённую информацию за день, неделю, месяц.

      Собственно, я посмотрел среднее количество ROWS за день для всех типов SCAN в рамках одного запроса.
      Другое дело, что, в случае с колоночными базами, понятие «ряды» очень-очень растяжимое.

      Например, следующие запросы могут отличаться по сложности в сто-двести раз, но при этом формально сканировать одно и то же количество «рядов».

      SELECT id FROM table;
      SELECT * FROM table;
      

      3. то есть базу данных требуется «разогревать», чтобы построить индексы?

      Не совсем. Индексы персистентные. Они сохраняются и после полного рестарта базы.

      Юзер ощущает построение индексов только в тот момент, когда он в самый первый раз пытается сделать JOIN по таким колонкам, которые за последний месяц ни разу не использовались для JOIN'а. После этого индекс поддерживается автоматически во время ETL-процесса (IMPORT, INSERT, UPDATE, DELETE, MERGE), и юзер этого процесса не видит.

      Что это за волшебные индексы — великий вопрос. Я много раз задавал его самым разным людям внутри Exasol и даже лично беседовал с их автором: Falko Mattasch. No luck! Подробностей они не рассказывают и очень тщательно охраняют этот секрет.

      4. то есть разработчик не имеет над этим контроля? Или все таблицы по дефолту реплицируются везде, и разработчик должнен явно указывать 'distribute by' для больших таблиц? Может ли быть несколько колонок в distribute by? Чем еще управляет разработчик в физическом дизайне?

      В физическом плане разработчик может только указать DISTRIBUTE BY для больших таблиц. Можно делать distribute по нескольким колонкам, но тогда они все вместе должны участвовать в JOIN'е.

      Если DISTRIBUTE BY явно не указан, то ряды случайно раскидываются по нодам таким образом, чтобы везде было поровну. Если баланс в силу каких-то причин нарушается, то в профилировщике можно увидеть фазу REDISTRIBUTE, которая его восстанавливает.

      Особо маленькие таблицы дополнительно полностью загружаются в память каждой ноды для того, чтобы всегда делать к ним локальный JOIN. Существует параметр, который позволяет управлять тем, что считается «маленькой таблицей» в данном случае, но он задаётся сразу на весь кластер.


      1. alexzaitsev
        01.12.2015 14:20

        > Судя по этим данным, в Badoo ETL процесс практически не вытесняет кеш.

        То есть вы загружаете данные, на чаще, чем раз в час, правильно я понимаю? А realtime аналитику не пробовали делать? Возможно?

        >Например, следующие запросы могут отличаться по сложности в сто-двести раз, но при этом формально сканировать одно и то же количество «рядов».

        Вообще, в колоночной базе количество строк — это не самый объективный параметр :) Но лучше не придумали, я пробовал оценивать по количеству «ячеек» — то есть строки умножить на столбцы, это несколько адекватнее, но такую статистику сложнее получить.

        >Подробностей они не рассказывают и очень тщательно охраняют этот секрет.

        Обычно это означает, что особого секрета нет :) Но судя по тому, что поддерживается только «равно» — это хеш.

        >Если DISTRIBUTE BY явно не указан, то ряды случайно раскидываются по нодам таким образом, чтобы везде было поровну.

        >Особо маленькие таблицы дополнительно полностью загружаются в память каждой ноды для того, чтобы всегда делать к ним локальный JOIN. Существует параметр, который позволяет управлять тем, что считается «маленькой таблицей» в данном случае, но он задаётся сразу на весь кластер.


        Ясно. В данных условиях разумно.

        Спасибо за ответы!


        1. wildraid
          01.12.2015 15:00

          То есть вы загружаете данные, на чаще, чем раз в час, правильно я понимаю? А realtime аналитику не пробовали делать? Возможно?

          Есть данные, которые загружаются каждые 15 минут. При этом для ETL используем целиком собственное программное решение, которое позволяет дробить загрузку как угодно. Можно хоть раз в минуту загружать, только практического смысла мало.

          Глобально по realtime аналитике задавал вопрос их основателю. Ответ был буквально следующий: «Последние блоки колонок не сжаты, можно часто импортировать по чуть-чуть рядов, существенного оверхеда на это нет».

          Но лучше не придумали, я пробовал оценивать по количеству «ячеек» — то есть строки умножить на столбцы, это несколько адекватнее, но такую статистику сложнее получить.

          На самом деле, даже это не подходит. Exasol в большинстве случаев работает со сжатыми данными, не разжимая их и не получая оригинальные значения в процессе выполнения. Это означает, что колонка, в которой всего 5-10 вариантов значений, сожмётся в разы лучше, чем колонка, в которой десятки тысяч вариантов. При этом формально количество «ячеек» будет одинаковым.

          Вопрос оценки сложности выполнения запроса в аналитической базе достоин отдельной большой статьи. Я так троллил других вендоров во время PoC, когда специально писал генератор с очень неудобной селективностью на VARCHAR, а потом спрашивал: «А почему так медленно?» :)

          Но судя по тому, что поддерживается только «равно» — это хеш.

          Мне всё же кажется, что это не хеш. По крайней мере, не классический хеш, как в row-based базах данных.

          Сужу по тому признаку, что JOIN по уже существующему индексу ест очень мало памяти. Никакая хеш таблица в такой объём не влезла бы. Также Exasol без проблем делает streaming частично завершённого JOIN'а в какой-нибудь дальнейший GROUP BY. Как это было бы возможно без предварительной сортировки данных — не очень представляю. Больше склоняюсь к какому-то очень хитрому распределённому Merge Join, но это всё чистой воды теория.

          Есть одна интересная статья по поводу индексов в аналитике:
          www.vldb.org/pvldb/vol7/p97-schuhknecht.pdf

          В ней упоминаются волшебные вещи: ART-tree, AVL-tree, CSB-tree, FAST.
          Похоже, мир академических деревьев намного обширнее, чем мы могли бы представить с юзерской точки зрения. :)


          1. alexzaitsev
            01.12.2015 15:14

            >Есть данные, которые загружаются каждые 15 минут. При этом для ETL используем целиком собственное программное решение, которое позволяет дробить загрузку как угодно. Можно хоть раз в минуту загружать, только практического смысла мало.

            Так мой вопрос был изначально про кеш. Если данные грузятся в таблицу часто, то что происходит с кешем. Скажем, добавилось в очередной загрузке новое значение в колоноку — надо полностью перестраивать, если я правильно понимаю.

            >На самом деле, даже это не подходит. Exasol в большинстве случаев работает со сжатыми данными, не разжимая их и не получая оригинальные значения в процессе выполнения. Это означает, что колонка, в которой всего 5-10 вариантов значений, сожмётся в разы лучше, чем колонка, в которой десятки тысяч вариантов. При этом формально количество «ячеек» будет одинаковым.

            Если мы хотим что-то сравнивать, то это нормальный критерий для колоночных баз данных. Что там как сожмется — второй вопрос. Кстати, если ли возможность управлять, как именно колонка сжимается, или это все внутри и автоматом?

            >Сужу по тому признаку, что JOIN по уже существующему индексу ест очень мало памяти. Никакая хеш таблица в такой объём не влезла бы.

            А что значит «ест»? Если в процессе выполнения запроса, то дополнительная память не нужна, индекс же уже в памяти (или на диске, наверное, тоже может быть, да?). А сколько занимает сам индекс, можно оценить?

            Мир экзотических деревьев, конечно, существует, но кроме LSM-деревьев и TokuDB Fractal-trees, по-моему ничего в боевых системах не используется (spatial-индексы не берем). Для merge join необходима сортировка, что дорого на больших данных.


            1. wildraid
              01.12.2015 15:46

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

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

              Кстати, если ли возможность управлять, как именно колонка сжимается, или это все внутри и автоматом?

              Внутри и автоматом. В какой-то документации видел информацию о том, что алгоритм компрессии колонки определяется по первым 100.000 рядам. Если у юзера есть подозрение, что всё сжато недостаточно хорошо, то есть команда RECOMPRESS. На практике пользовались 2-3 раза, много не выиграли.

              Если в процессе выполнения запроса, то дополнительная память не нужна, индекс же уже в памяти (или на диске, наверное, тоже может быть, да?). А сколько занимает сам индекс, можно оценить?

              Действительно, индекс уже и на диске, и в памяти. Для индекса используются такие же блоки, как и для данных. И он точно так же лежит в кеше.

              Вот живой пример сделал. Запрос такой:
              SELECT count(distinct a.activity_user_id) 
              FROM ingres.F_ACTIVITY_BY_PLATFORM a 
                  JOIN ingres.F_ACTIVITY_PASSIVE b ON (a.activity_user_id=b.activity_user_id AND a.activity_dt=b.activity_dt);
              


              Результат профилирования:

              Крупно: habrastorage.org/files/bb4/211/538/bb4211538754475aacc057e5e74bd7fe.png

              Самый простой JOIN, 14 миллиардов на 47 миллиардов, простая группировка. Выполняется чуть больше минуты.

              DURATION — это время выполнения стадии в секундах.
              TEMP_DB_RAM_PEAK — это сколько максимально было памяти использовано в мегабайтах.
              HDD_READ — сколько диска прочитали (в данном случае всё из кеша)
              NET — сколько данных передано по сети (в данном случае 0, потому что локальный JOIN)

              Индекс суммарно занимает 64Гб. Всего лишь по 8Гб на одну ноду.


              1. alexzaitsev
                01.12.2015 16:49

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

                Ок, логично. На самом деле в Вертике то же самое происходит, только этим занимается кэш файловой системы (блок = файл), поэтому об этом мало кто знает :)

                >Результат профилирования:
                Интересно, а почему в 3 и 4й строчке out rows меньше, чем in?

                Кстати из этого результата не очевидно, как индексы используется (и используются ли вообще). А какая селективность индекса (или кардинальность колонок) activity_user_id и activity_dt?


                1. wildraid
                  01.12.2015 17:08

                  Интересно, а почему в 3 и 4й строчке out rows меньше, чем in?

                  А это inner join. Вернулись только те ряды, которые существуют и в первой таблице, и во второй.

                  Кстати из этого результата не очевидно, как индексы используется (и используются ли вообще).

                  Там есть поле REMAKRS, которое я сократил, потому что оно длинное. На 4-ой стадии его значение выглядит вот так:

                  F_ACTIVITY_BY_PLATFORM(ACTIVITY_DT,ACTIVITY_USER_ID) => LOCAL INDEX (ACTIVITY_DT,ACTIVITY_USER_ID)
                  

                  Вообще Exasol всегда делает нормальный JOIN только по индексу. Других сценариев у него нет.

                  А какая селективность индекса (или кардинальность колонок) activity_user_id и activity_dt?

                  Несколько сотен миллионов юзеров. Тысяча с хвостиком дат.
                  Точные цифры не буду приводить, чтобы не поругали потом :)


                  1. alexzaitsev
                    01.12.2015 17:47

                    >Несколько сотен миллионов юзеров. Тысяча с хвостиком дат.

                    Тогда 64GB на индекс не выглядит чем-то очень маленьким. Впрочем, если памяти много, то почему бы и нет :)

                    Спасибо за разговор. Приятно поговорить с умным человеком :)


                1. wildraid
                  01.12.2015 17:39

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

                  Берём вот такой запрос:

                  SELECT count(*)
                  FROM bi_financial.F_ROUTINGLOG a
                  	LEFT JOIN ingres.F_INCOMELOG_AUDIT b ON (REGEXP_SUBSTR(a.external_id, '^(\d+)-(\d+)-(\d+)-(\d+)') = REGEXP_SUBSTR(b.external_id, '^(\d+)-(\d+)-(\d+)-(\d+)'));
                  

                  JOIN по регулярному выражению.

                  Профайлинг:

                  Крупно: habrastorage.org/files/9ff/791/f17/9ff791f17ac54c01979404cdc122e68b.png

                  За 4 секунды динамически создаётся индекс по Expression, и сразу же используется для последующего JOIN.
                  Для разового анализа колонку материализовывать не обязательно.


                  1. alexzaitsev
                    01.12.2015 17:53

                    Хм, то есть за 4 секунды читается колонка, вычисляется регулярное выражение и строится индекс. Вторая таблица маленькая, для нее только вычисляется выражение. Потом идет джойн, используя уже построенный индекс (кстати то, что он строится только для одной таблицы, еще один аргумент в пользу хеша: для merge надо строить с двух сторон). То есть, насколько я понимаю, это как раз пример, когда заранее построенный индекс НЕ используется, а строится динамически в памяти.


                    1. wildraid
                      01.12.2015 18:09

                      Да, всё совершенно правильно.

                      Такой индекс отличается от обычного только тем, что он будет автоматически дропнут после завершения выполнения запроса. Это потому, что в Exasol нет понятий IMMUTABLE и VOLATILE, и он не может гарантировать, что, условно, 2 + 2 всегда равно 4. Каждый раз нужно заново посчитать и убедиться.

                      Но строится всё это реально очень быстро и прозрачно для пользователя. На практике это означает, что аналитики могут сделать любой анализ в 90% случаев самостоятельно. Даже такой, который не был задуман изначально. И который в страшном сне не мог присниться никому заранее.

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


  1. alexzaitsev
    01.12.2015 12:56

    А вот еще вопрос:

    «3. Fault tolerance. В своей практике мы сталкивались с несколькими аварийными сценариями. Например, заканчивалось свободное место, или кто-то случайно физически отключал часть работающих серверов от сети. Никаких существенных проблем с этим мы не заметили. База заранее пишет о проблеме, останавливается и ждет исправления ситуации. „

    То есть база не может работать хотя бы без одной ноды? Ей необходимы все?


    1. wildraid
      01.12.2015 13:50
      +1

      Зависит от параметра «redundancy». Например, при «redundancy=2» можно потерять одну ноду. При «redundancy=3» можно потерять две ноды. При «redundancy=1» нельзя потерять ни одной, но зато всё место на дисках — ваше.

      На самом деле, всё немного сложнее. Exasol представляет собой объединение нескольких сервисов. Например, ExaSolution управляет SQL запросами и предоставляет интерфейс для юзеров. ExaStorage управляет хранением данных, дисками, кешами. ExaOperation осуществляет общение нод друг с другом, управляет лицензиями. Есть сервис для логирования, для синхронизации времени, и т.д.

      В некоторых очень редких случаях возможно такое, что один из сервисов начинает глючить, теряет кворум, пишет warning'и какие-нибудь. Но это всё либо само восстанавливается, либо быстро патчится. С какими-то серьёзными проблемами пока не сталкивались, данные не теряли.


      1. alexzaitsev
        01.12.2015 14:10

        Ага, насчет redundancy понятно, спасибо.

        Насчет сервисов — это не столь существенно, вопрос реализации и именования.