Apache druid - одно из самых популярных решений с открытым исходным кодом для онлайн-аналитической обработки (OLAP). Его используют многие технологические компании, такие как Airbnb и Netflix, для выполнения запросов к потокам данных, содержащих миллионы событий в минуту. Это позволяет компаниям принимать решения практически в режиме реального времени.

Основным преимуществом druid является то, что он легко масштабируется до миллиона оборотов в минуту записи и выше с задержкой менее секунды, а также высокодоступен в течение всей операции. Но в наши дни многие базы данных имеют высокую доступность и задержку менее секунды, так что же отличает Druid от других?

Реляционные хранилища и хранилища ключей / ценностей против Druid

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

Что касается баз данных ключей и значений NoSQL, агрегированные вычисления, безусловно, будут неэффективными, так как вам нужно будет запросить несколько разделов на нескольких узлах. Вы можете обойти это, предварительно вычислив данные с некоторой степенью детализации (например, 1 мин.) И сохранив их с ключом. Однако, следуя этому подходу, вы теряете возможность выполнять исследование в нескольких окнах. Хранение агрегатов для всех возможных комбинаций столбцов также невозможно, так как это приводит к экспоненциальному увеличению требований к хранению.

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

Как это работает?

Друид состоит из нескольких узлов, каждый из которых играет свою роль. Все эти узлы работают в гармонии друг с другом (в основном с использованием Apache Zookeeper для координации) для обеспечения производительности.

Поговорим о каждом из этих узлов более подробно. Если вы уже знакомы с узлами и их взаимодействием, вы можете сразу перейти к последнему разделу.

В реальном времени (менеджеры среднего звена)

Эти узлы отвечают за обработку данных в реальном времени как для чтения, так и для записи. В частности, запись состоит из 4 основных этапов:

  1. Ingest - когда данные записываются в druid, они сначала попадают в индексный буфер в памяти этого узла. Этот буфер основан на куче, а события хранятся по строкам.
  2. Сохранять - чтобы избежать переполнения кучи, этот индекс периодически сохраняется на диске для обеспечения надежности. Буфер в памяти преобразуется в формат хранения, ориентированный на столбцы, и становится неизменным. Затем постоянный индекс загружается в память вне кучи для более быстрых запросов.
  3. Слияние - периодическая фоновая задача объединяет неизменяемые блоки в так называемый сегмент.
  4. Передача обслуживания - сегменты, наконец, загружаются в распределенные хранилища данных (называемые глубокими хранилищами), такие как HDFS, для большей надежности и доступности. Он также обновляет метаданные сегмента в MySQL, чтобы их могли видеть другие узлы.

Исторический

Эти узлы загружают сегменты из глубокого хранилища, а затем обслуживают запросы поверх него. Большинство аналитических запросов, которые выполняются на druid, большую часть времени отправляются на эти узлы. Следовательно, эти узлы являются основными рабочими кластеров.

Узлы получают информацию о любых новых сегментах, публикуемых в глубоком хранилище, от zookeeper. Затем сегмент загружается и загружается для обслуживания. Узлы также кэшируют некоторые сегменты на локальном диске, что позволяет им быстро обслуживать запросы в случае перезапуска. Узлы также обеспечивают согласованность чтения, поскольку имеют дело только с неизменяемыми сегментами.

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

Доступность зоопарков не позволяет этим узлам загружать новые сегменты, но старые сегменты продолжают обслуживаться без простоев.

Брокеры

Все запросы пользователей поступают на узлы брокера. Затем эти узлы перенаправляют запросы на соответствующие исторические узлы и узлы реального времени, объединяют оба результата и отправляют их обратно. Узлы также поддерживают кэш LRU в памяти (который можно изменить для использования Memcached). Кэш содержит результаты по сегментам. Однако результаты только для исторических сегментов узлов являются кешами, поскольку данные в реальном времени будут сохранять изменения довольно часто.

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

Координатор

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

  • Загрузите новые сегменты, опубликованные узлами реального времени в HDFS.
  • Удалите устаревшие данные.
  • Реплицируйте данные для избыточности, чтобы вы могли терпеть отказы узлов.
  • Данные балансировки нагрузки между несколькими узлами.

Только один узел-координатор выбирается в качестве лидера, и он отвечает за всю операцию, в то время как остальные узлы действуют только как резервные.

Координатор получает последнее состояние кластера из Zookeeper и информацию о сегментах, которые должны обслуживаться из MySQL. Отказ MySQL, а также zookeeper затрудняют возможность назначать или удалять новые сегменты, но старые сегменты по-прежнему доступны для запросов.

Так что же отличает его от конкурентов?

Разделение ответственности

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

Колонно-ориентированное хранилище

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

Предотвращение ненужных сканирований

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

Инвертированный индекс приведенной выше таблицы будет выглядеть так:

Foo: [1,0,0,1,0,1]

Бар: [0,1,1,0,1,0]

где 1 означает, что конкретный ключ присутствует в строке индекса. Если вы хотите просканировать все строки, содержащие Foo и Bar, просто возьмите OR для обоих индексов.

Оценка мощности

Чтобы получить точные агрегаты мощности, такие как определение количества уникальных пользователей, посещающих ваш сайт каждую минуту, вам нужно будет сохранить пользователей в какой-то структуре данных (например, HashSet), а затем вычислить общее количество элементов в ней. Однако это требует много места.

С другой стороны, Druid выполняет его с помощью HyperLogLog, что дает примерно 97% точность. Обычно это нормально для большинства людей, выполняющих аналитические запросы. Вы даже можете сделать это быстрее, предварительно вычислив HLL во время загрузки во время индексирования.

Предварительная агрегация

Druid может предварительно агрегировать данные во время загрузки (известное как Rollup). Это уменьшает размер хранимых данных, а также значительно ускоряет запросы агрегирования. В этом случае вы теряете информацию о каждой строке, поэтому ее можно отключить во время приема.

Кеширование

Druid поддерживает кеш-сегмент запросов к брокерам, что помогает быстро возвращать результаты. Он также кэширует данные на серверах истории и реального времени для более быстрого сканирования.

Балансировка нагрузки

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

Разбиение по времени

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

Если вы хотите узнать больше о Druid, перейдите по ссылкам ниже:

  1. Официальная документация
  2. Druid: хранилище аналитических данных в реальном времени (технический документ)
  3. Введение в Druid, вашу интерактивную аналитику в (большом) масштабе
  4. MetaMarkets - Введение в Druid от Фанцзинь Яна на Youtube

Свяжитесь со мной в LinkedIn или Twitter или напишите письмо на [email protected]