В документации ClickHouse честно о них пишут. Навскидку:
1) Неэффективные большие JOIN'ы. Ограничения на sub-select'ы. Совсем произвольный отчёт из ста больше таблиц не так просто построить.
2) Свой SQL. Нужно вручную различать и понимать разницу между LOCAL \ GLOBAL JOIN, ANY \ ALL, WHERE \ PREWHERE. Непросто будет мигрировать на другой SQL-совместимый софт.
3) Нет транзакций, нет апдейтов в простой форме.
Но, если систему изначально строить с учётом этих особенностей, то всё решаемо.
Про память интересно. У нас чуть больше даже, но взлетает без патчей пока. Видимо, вопрос не только в объёме.
А попробуйте загружать без JDBC, он тормоз) В постгресе есть выгрузка в STDOUT в формате CSV. Пустите пару десятков таких, соберите вместе через fdlinecombine (https://github.com/vi/fdlinecombine) и отправьте общим потоком в exajload. Там можно из STDIN читать, если передать файл /dev/stdin. Без промежуточных файлов.
Или можно набросать простой скрипт, в котором сделать штук сто staging таблиц, залить в каждую из них данные из отдельной связки postgres -> exajload, а затем их вместе собрать в одну мега-таблицу через INSERT… SELECT. Так совсем параллельно будет.
Интересно как… буквально пару дней назад выдвинул похожее предложение. Суть проблемы примерно такая же — в любой момент может прилететь любое количество срочных задач. И делать в первую очередь нужно… все сразу, разумеется.
Суть предложения:
1) Выкинуть Scrum, выкинуть спринты, забыть про чёткие сроки.
2) Остановить частые переключения и скачки с задачи на задачу.
3) Создать открытые очереди задач.
Перед тем, как поставить новый срочный таск, заказчики обязательно видят срочную очередь. Если нужно побыстрее, то заказчики решают приоритеты между собой, не вмешивая команду.
Нельзя сделать одновременно всё моментально и хорошо. Но можно выдавать максимально возможный performance в рамках тех ресурсов, что есть.
Первая мысль: можно добавить к этому определение аномалий (e.g. «регистрации упали», «ошибки выросли») и рассылать сообщения о них сразу с отчётами и картинками.
По опыту, если сложный запрос написан без очевидных ошибок, но план выполнения подозрительный, то первым делом смотрю разницу между estimated rows и actual rows. Часто дело именно в неверном предположении о количестве возвращаемых рядов, которое потом может кратно умножаться по мере продвижения по дереву.
Извиняюсь, поздно заметил, что речь о сессиях теста, а не про EXA_ALL_SESSIONS. Комментарий уже не могу изменить.
Если сессии теста запускались синхронно, то первый результат 165 может объясняться тем, что каждая из восьми сессий строила собственные индексы в своей транзакции. Потом семь индексов из восьми выбросили, а один оставшийся был использован во втором запуске.
1) После загрузки данных и построения индексов есть смысл запустить RECOMPRESS. Коэффициент сжатия заметно улучшится. В обычной жизни Exasol сам периодически запускает его в background по мере роста объёма данных. Также последний блок в таблице хранится без сжатия для оптимизации быстрых маленьких INSERT'ов.
Проще говоря, чем больше таблица и чем чаще её используют, тем лучше будет сжатие. В некоторых случаях коэффициент может спокойно дойти до 10 и выше.
2) У Exasol на самом деле одна сессия выполняет запрос. Вторая сессия — это ExaPlus подключился, чтобы считывать мета-данные без конфликтов. Имена схем, таблиц, функций, вот это всё. Если во время выполнения теста посмотреть, чем она занята, то будет статус IDLE.
У нас много случаев, когда внешний софт создаёт гигантские запросы, в которых соединяет по 30+ таблиц. Больших и маленьких, с группировкой и без, с sub-select'ами и CTE. До тех пор, пока промежуточный результат остаётся в рамках разумного, всё хорошо работает.
За редким исключением, нет необходимости как-то трансформировать данные специально для наилучшей производительности. Проекций, pre join'ов, вручную создаваемых индексов нет. Есть DISTRIBUTE BY, который здорово помогает снизить нагрузку на сеть, но он не обязателен.
Не стоит сравнивать по этому предложению. По сути, это Exasol + Cloud + Tableau + Support.
Чистые лицензии Exasol'а куда более гибкие и могут не иметь ограничений по размеру загружаемых данных.
Всё не так однозначно. Лучше сравнивать конкретные предложения от обоих вендоров.
Free Small Business Edition — бесплатно, в том числе для коммерческого использования.
200GB памяти на одной ноде будут держать примерно 1Tb сырых данных без сжатия.
Также, видимо, ничто не мешает поднять несколько таких instance'ов и, при необходимости, делать между ними запросы через SELECT… FROM EXA (...).
Чуть побольше расскажу про Kognitio. Их главная проблема — rows based storage. Из-за этого намного хуже ситуация с компрессией и оптимизацией count\sum\аналитики. Но, если данные целиком влезают в память, то всё очень быстро.
У пользователя есть полный контроль над тем, что находится в памяти. Можно создавать проекции или какие-то определённые sub-set'ы и хранить их только in-memory, не дублируя данные на дисках. Есть богатые возможности и почти полный контроль над distribution данных по нодам.
У них очень хороший loader, прекрасный EXPLAIN (аж трёх видов), и вообще в целом качество софта производит отличное впечатление.
Минусы СУБД:
1) Меньше подходит для ad-hoc запросов.
2) Очень критична к количеству памяти.
3) Нужно больше железа, чем на Exasol.
4) Дорого (на мой взгляд, по состоянию 2-3 года назад).
1) Хорошо бы использовать более мощные физические серверы, если есть возможность их собрать. Особенно в плане памяти, 128Gb маловато уже.
2) Побольше бы данных. 500 миллионов для современных СУБД — на один зуб. Миллиардах на десяти разница между решениями куда более существенная. Bottleneck'и лучше видно.
3) Пример D2 очень хороший. И даже не тем, что сразу несколько колонок используется, а тем, что он заставляет СУБД разжимать значения и вычислять результат отдельно для каждого ряда. «Волшебная оптимизиация» перестаёт работать, и можно увидеть реалистичные результаты. Многие решения этот тест проваливают.
Другой вариант этого теста — сложный LIKE:
WHERE foo LIKE '%' || bar || '%'
4) По-возможности, даты лучше вычислять в скрипте и передавать в запросы в готовом виде. Например:
Сейчас всё больше решений могут использовать такие простые фильтры, чтобы читать меньше данных и ускорить выполнение запросов в разы. Грех не пользоваться.
Очень люблю PostgreSQL, но, как мне кажется, зря он пошёл в этом направлении.
Существуют аналитические СУБД, которые изначально были построены с расчетом на параллельное выполнение запросов на множестве ядер или даже множестве серверов. Там всё заточено под это: и способ хранения, и компрессия, и протоколы, и планы выполнения.
А тут такой Постгрес выходит и говорит, что он теперь поддерживает 5% подобных функций, а через годик-другой будет поддерживать 20%. И то путём титанических усилий по переделке ядра, которое никогда не задумывалось для параллельного выполнения.
Ну и какой практический смысл в этом? Я бы лучше сконцентрировался на том, в чём PG по-настоящему силён, а аналитику оставил бы для специализированных продуктов.
Такой индекс отличается от обычного только тем, что он будет автоматически дропнут после завершения выполнения запроса. Это потому, что в Exasol нет понятий IMMUTABLE и VOLATILE, и он не может гарантировать, что, условно, 2 + 2 всегда равно 4. Каждый раз нужно заново посчитать и убедиться.
Но строится всё это реально очень быстро и прозрачно для пользователя. На практике это означает, что аналитики могут сделать любой анализ в 90% случаев самостоятельно. Даже такой, который не был задуман изначально. И который в страшном сне не мог присниться никому заранее.
Если же окажется, что такой JOIN нужно делать постоянно, то нет проблем материализовать куб или добавить ещё одну колонку к основной таблице. И тогда индекс не будет умирать. Но обычно такое редко нужно.
Я хочу ещё один пример показать, как иллюстрацию того, что тема с индексами идёт сильно дальше, чем кажется на первый взгляд.
Берём вот такой запрос:
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+)'));
За 4 секунды динамически создаётся индекс по Expression, и сразу же используется для последующего JOIN.
Для разового анализа колонку материализовывать не обязательно.
Если данные грузятся в таблицу часто, то что происходит с кешем. Скажем, добавилось в очередной загрузке новое значение в колоноку — надо полностью перестраивать, если я правильно понимаю.
Судя по всему, кеш представляет собой просто те же самые блоки, которые хранятся на диске, только загруженные в память. Добавление нового значения не приводит к перестройке старых блоков и, соответственно, не дропает уже закешированные старые блоки. Возможно, есть какие-то сценарии, когда это всё же происходит, но в общей практике я не замечал.
Кстати, если ли возможность управлять, как именно колонка сжимается, или это все внутри и автоматом?
Внутри и автоматом. В какой-то документации видел информацию о том, что алгоритм компрессии колонки определяется по первым 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);
Самый простой JOIN, 14 миллиардов на 47 миллиардов, простая группировка. Выполняется чуть больше минуты.
DURATION — это время выполнения стадии в секундах.
TEMP_DB_RAM_PEAK — это сколько максимально было памяти использовано в мегабайтах.
HDD_READ — сколько диска прочитали (в данном случае всё из кеша)
NET — сколько данных передано по сети (в данном случае 0, потому что локальный JOIN)
Индекс суммарно занимает 64Гб. Всего лишь по 8Гб на одну ноду.
1) Неэффективные большие JOIN'ы. Ограничения на sub-select'ы. Совсем произвольный отчёт из ста больше таблиц не так просто построить.
2) Свой SQL. Нужно вручную различать и понимать разницу между LOCAL \ GLOBAL JOIN, ANY \ ALL, WHERE \ PREWHERE. Непросто будет мигрировать на другой SQL-совместимый софт.
3) Нет транзакций, нет апдейтов в простой форме.
Но, если систему изначально строить с учётом этих особенностей, то всё решаемо.
А попробуйте загружать без JDBC, он тормоз) В постгресе есть выгрузка в STDOUT в формате CSV. Пустите пару десятков таких, соберите вместе через fdlinecombine (https://github.com/vi/fdlinecombine) и отправьте общим потоком в exajload. Там можно из STDIN читать, если передать файл /dev/stdin. Без промежуточных файлов.
Или можно набросать простой скрипт, в котором сделать штук сто staging таблиц, залить в каждую из них данные из отдельной связки postgres -> exajload, а затем их вместе собрать в одну мега-таблицу через INSERT… SELECT. Так совсем параллельно будет.
Суть предложения:
1) Выкинуть Scrum, выкинуть спринты, забыть про чёткие сроки.
2) Остановить частые переключения и скачки с задачи на задачу.
3) Создать открытые очереди задач.
Перед тем, как поставить новый срочный таск, заказчики обязательно видят срочную очередь. Если нужно побыстрее, то заказчики решают приоритеты между собой, не вмешивая команду.
Нельзя сделать одновременно всё моментально и хорошо. Но можно выдавать максимально возможный performance в рамках тех ресурсов, что есть.
https://www.depesz.com/2011/05/20/pagination-with-fixed-order/
Конечно, при помощи сложных ухищрений и со множеством ограничений. Но, если какой-то сервис крутится только вокруг pagination, это можно сделать.
Если сессии теста запускались синхронно, то первый результат 165 может объясняться тем, что каждая из восьми сессий строила собственные индексы в своей транзакции. Потом семь индексов из восьми выбросили, а один оставшийся был использован во втором запуске.
1) После загрузки данных и построения индексов есть смысл запустить RECOMPRESS. Коэффициент сжатия заметно улучшится. В обычной жизни Exasol сам периодически запускает его в background по мере роста объёма данных. Также последний блок в таблице хранится без сжатия для оптимизации быстрых маленьких INSERT'ов.
Проще говоря, чем больше таблица и чем чаще её используют, тем лучше будет сжатие. В некоторых случаях коэффициент может спокойно дойти до 10 и выше.
2) У Exasol на самом деле одна сессия выполняет запрос. Вторая сессия — это ExaPlus подключился, чтобы считывать мета-данные без конфликтов. Имена схем, таблиц, функций, вот это всё. Если во время выполнения теста посмотреть, чем она занята, то будет статус IDLE.
У нас много случаев, когда внешний софт создаёт гигантские запросы, в которых соединяет по 30+ таблиц. Больших и маленьких, с группировкой и без, с sub-select'ами и CTE. До тех пор, пока промежуточный результат остаётся в рамках разумного, всё хорошо работает.
За редким исключением, нет необходимости как-то трансформировать данные специально для наилучшей производительности. Проекций, pre join'ов, вручную создаваемых индексов нет. Есть DISTRIBUTE BY, который здорово помогает снизить нагрузку на сеть, но он не обязателен.
Чистые лицензии Exasol'а куда более гибкие и могут не иметь ограничений по размеру загружаемых данных.
Всё не так однозначно. Лучше сравнивать конкретные предложения от обоих вендоров.
200GB памяти на одной ноде будут держать примерно 1Tb сырых данных без сжатия.
Также, видимо, ничто не мешает поднять несколько таких instance'ов и, при необходимости, делать между ними запросы через SELECT… FROM EXA (...).
У пользователя есть полный контроль над тем, что находится в памяти. Можно создавать проекции или какие-то определённые sub-set'ы и хранить их только in-memory, не дублируя данные на дисках. Есть богатые возможности и почти полный контроль над distribution данных по нодам.
У них очень хороший loader, прекрасный EXPLAIN (аж трёх видов), и вообще в целом качество софта производит отличное впечатление.
Минусы СУБД:
1) Меньше подходит для ad-hoc запросов.
2) Очень критична к количеству памяти.
3) Нужно больше железа, чем на Exasol.
4) Дорого (на мой взгляд, по состоянию 2-3 года назад).
1) Хорошо бы использовать более мощные физические серверы, если есть возможность их собрать. Особенно в плане памяти, 128Gb маловато уже.
2) Побольше бы данных. 500 миллионов для современных СУБД — на один зуб. Миллиардах на десяти разница между решениями куда более существенная. Bottleneck'и лучше видно.
3) Пример D2 очень хороший. И даже не тем, что сразу несколько колонок используется, а тем, что он заставляет СУБД разжимать значения и вычислять результат отдельно для каждого ряда. «Волшебная оптимизиация» перестаёт работать, и можно увидеть реалистичные результаты. Многие решения этот тест проваливают.
Другой вариант этого теста — сложный LIKE:
4) По-возможности, даты лучше вычислять в скрипте и передавать в запросы в готовом виде. Например:
заменить на:
Сейчас всё больше решений могут использовать такие простые фильтры, чтобы читать меньше данных и ускорить выполнение запросов в разы. Грех не пользоваться.
Существуют аналитические СУБД, которые изначально были построены с расчетом на параллельное выполнение запросов на множестве ядер или даже множестве серверов. Там всё заточено под это: и способ хранения, и компрессия, и протоколы, и планы выполнения.
А тут такой Постгрес выходит и говорит, что он теперь поддерживает 5% подобных функций, а через годик-другой будет поддерживать 20%. И то путём титанических усилий по переделке ядра, которое никогда не задумывалось для параллельного выполнения.
Ну и какой практический смысл в этом? Я бы лучше сконцентрировался на том, в чём PG по-настоящему силён, а аналитику оставил бы для специализированных продуктов.
Такой индекс отличается от обычного только тем, что он будет автоматически дропнут после завершения выполнения запроса. Это потому, что в Exasol нет понятий IMMUTABLE и VOLATILE, и он не может гарантировать, что, условно, 2 + 2 всегда равно 4. Каждый раз нужно заново посчитать и убедиться.
Но строится всё это реально очень быстро и прозрачно для пользователя. На практике это означает, что аналитики могут сделать любой анализ в 90% случаев самостоятельно. Даже такой, который не был задуман изначально. И который в страшном сне не мог присниться никому заранее.
Если же окажется, что такой JOIN нужно делать постоянно, то нет проблем материализовать куб или добавить ещё одну колонку к основной таблице. И тогда индекс не будет умирать. Но обычно такое редко нужно.
Берём вот такой запрос:
JOIN по регулярному выражению.
Профайлинг:
Крупно: habrastorage.org/files/9ff/791/f17/9ff791f17ac54c01979404cdc122e68b.png
За 4 секунды динамически создаётся индекс по Expression, и сразу же используется для последующего JOIN.
Для разового анализа колонку материализовывать не обязательно.
А это inner join. Вернулись только те ряды, которые существуют и в первой таблице, и во второй.
Там есть поле REMAKRS, которое я сократил, потому что оно длинное. На 4-ой стадии его значение выглядит вот так:
Вообще Exasol всегда делает нормальный JOIN только по индексу. Других сценариев у него нет.
Несколько сотен миллионов юзеров. Тысяча с хвостиком дат.
Точные цифры не буду приводить, чтобы не поругали потом :)
Судя по всему, кеш представляет собой просто те же самые блоки, которые хранятся на диске, только загруженные в память. Добавление нового значения не приводит к перестройке старых блоков и, соответственно, не дропает уже закешированные старые блоки. Возможно, есть какие-то сценарии, когда это всё же происходит, но в общей практике я не замечал.
Внутри и автоматом. В какой-то документации видел информацию о том, что алгоритм компрессии колонки определяется по первым 100.000 рядам. Если у юзера есть подозрение, что всё сжато недостаточно хорошо, то есть команда RECOMPRESS. На практике пользовались 2-3 раза, много не выиграли.
Действительно, индекс уже и на диске, и в памяти. Для индекса используются такие же блоки, как и для данных. И он точно так же лежит в кеше.
Вот живой пример сделал. Запрос такой:
Результат профилирования:
Крупно: habrastorage.org/files/bb4/211/538/bb4211538754475aacc057e5e74bd7fe.png
Самый простой JOIN, 14 миллиардов на 47 миллиардов, простая группировка. Выполняется чуть больше минуты.
DURATION — это время выполнения стадии в секундах.
TEMP_DB_RAM_PEAK — это сколько максимально было памяти использовано в мегабайтах.
HDD_READ — сколько диска прочитали (в данном случае всё из кеша)
NET — сколько данных передано по сети (в данном случае 0, потому что локальный JOIN)
Индекс суммарно занимает 64Гб. Всего лишь по 8Гб на одну ноду.