Аналитический движок Amazon Redshift + преимущества Облака


Привет, Хабр!


На связи Артемий Козырь из команды Аналитики, и я продолжаю знакомить вас с Wheely. В этом выпуске:


  • Основы гибких кластерных вычислений
  • Колоночное хранение и компрессия данных
  • Вместо индексов: ключи сегментации и сортировки
  • Управление доступами, правами, ресурсами
  • Интеграция с S3 или Даталейк на ровном месте

Все аналитические решения Wheely так или иначе строятся на основе Хранилища Данных, движком которого выступает продукт с кодовым названием Redshift от провайдера облачных сервисов Amazon. Но почему именно этот продукт?


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


Кластерные вычисления как основа архитектуры


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


Redshift обеспечивает параллельную обработку данных на кластере машин


Redshift обеспечивает параллельную обработку данных на кластере машин


Этот принцип широко известен как MPP (massively parallel processing, массово-параллельная архитектура). Не лишним будет упомянуть и другие популярные СУБД, использующие подобные принципы: BigQuery, Vertica, Greenplum, Teradata, Azure SQL DW.


Каждая compute node обладает набором выделенных ресурсов: CPU, memory, disk, которые определяются типом виртуальной машины.


Спецификация типов compute node доступных для Amazon Redshift


Спецификация типов compute node доступных для Amazon Redshift


Сегодня в Wheely мы используем 8 узлов типа dc2.large, что в сумме составляет 16 vCPU, 120 GiB памяти, 1.3 TB объем диска. Кто-то может справедливо заметить, что это не такие уж и огромные объемы данных, на что я бы возразил: суть не в объеме, а в том, какие результаты и пользу вы можете выжать из этих данных.


Не могут не радовать элементы гибкости и эластичности (elasticity), в целом присущие облачным сервисам. С ростом бизнеса и потребности в аналитике несколько раз без особого труда производилось расширение кластера: увеличивалось количество узлов (resize), осуществлялся переход на новое поколение виртуальных машин (node upgrade).


Concurrency Scaling позволит поднять временный кластер для того чтобы справиться с пиковой нагрузкой


Concurrency Scaling позволит поднять временный кластер для того чтобы справиться с пиковой нагрузкой


В моменты пиковых нагрузок производительность на стабильном уровне может поддержать функция concurrency scaling, которая автоматически добавляет вычислительные мощности в виде временных compute nodes, которые "гасятся" после спада нагрузки.


Колоночное хранение и компрессия данных


В противовес классическим операционным СУБД (PostgreSQL, MySQL, SQL Server), хранящим данные в строковом формате (row-oriented storage), Redshift использует все преимущества колоночного хранения (column-oriented storage). Блоки на диске заполняются данными одной колонки, и каждая колонка хранится отдельно от любой другой.


Это дает несколько неоспоримых преимуществ для аналитических нагрузок. Прежде всего для операций проекции (запросе каких-то конкретных колонок). Представьте себе широкую таблицу фактов из 100+ колонок; для формирования результата запроса нам из них понадобятся только 5. При колоночном хранении мы с диска прочтем ровно 5 запрашиваемых колонок. При строковом хранении пришлось бы читать все 100+ колонок и потом отбросить бОльшую часть, что в разы увеличивает интенсивность I/O и нагрузку на диск.


Колоночное хранение организовывает в блоки данные одной колонки (не строки!)


Колоночное хранение организовывает в блоки данные одной колонки (не строки!)


Во-вторых, будучи однородными, данные одной колонки весьма успешно поддаются сжатию (compression). Объясняя на пальцах, нет необходимости хранить наименование тарифа (Business, First, Luxe) для каждой поездки. Достаточно сделать одну запись и указать, на какое количество строк будет распространяться это значение: {100, Luxe}. Эффект становится особенно заметен, когда количество строк исчисляется сотнями тысяч и миллионами. На деле всё несколько сложнее: кодеков (алгоритмов) сжатия около десятка, и у каждого есть свои особенности и лучшие сценарии для применения.


Пример сжатия данных алгоритмом Runlength encoding


Пример сжатия данных алгоритмом Runlength encoding


Однако при должном усердии можно добиться еще более значительного сжатия, чем дефолтные кодеки, выбранные Amazon Redshift. Чуть подробнее этот вопрос я раскрывал в публикации Compressing Redshift columnar data even further with proper encodings.


Пересмотр алгоритмов сжатия данных позволил сэкономить 21% дискового пространства


Пересмотр алгоритмов сжатия данных позволил сэкономить 21% дискового пространства


Ключи сегментации и сортировки вместо индексов


В Redshift вторичные поисковые структуры данных (индексы) в привычном понимании (B-Tree, Bitmap) отсутствуют. Здесь они просто не нужны, и вот почему:


Тонкая настройка позволяет задать принцип, по которому строки будут распределены между узлами кластера: равномерное (EVEN), копия на каждом (ALL), или по ключу (KEY). Эта конфигурация носит название тип сегментации (distribution style).


3 типа распределения каждой строки данных в кластере: KEY, ALL, EVEN


3 типа распределения каждой строки данных в кластере: KEY, ALL, EVEN


А уже данные в блоках на каждом узле можно физически хранить в заданном порядке, то есть отсортированными. На ум приходит сортировка, например, по монотонно возрастающему идентификатору. Конфигурация, использующая один и тот же атрибут в distribution key, sort key обеспечит использование самого оптимального типа соединения таблиц — SORT MERGE JOIN:


ORDER BY request_id:
{{
   config(
       dist='request_id',
       sort='request_id'
   )
}}

Смотрите на это как на конструкцию ORDER BY в SQL-запросе, сохраняющую порядок в таблице на диске. При этом задействованы могут быть несколько колонок — тогда это уже составной ключ (compound sort key).


ORDER BY city, date, product_name:
{{
   config(
       materialized='table',
       dist="city",
       sort=['city', 'date', 'product_name']
   )
}}

В дополнение, есть еще такой очень хитрый вид сортировки как Interleaved sort key, который дает одинаковый вес любой из колонок (или их комбинации) в ключе сортировки. Он отлично подходит к витринам данных, для которых нет одного заранее известного паттерна доступа.


INTERLEAVED SORT BY completed_ts_loc, city, country, service_group, is_airport, is_wheely_journey:
{{
   config(
       materialized='table',
       dist="journey_id",
       sort_type='interleaved',
       sort=["completed_ts_loc"
               , "city"
               , "country"
               , "service_group"
               , "is_airport"
               , "is_wheely_journey"]
   )
}}

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


Разграничение прав доступа и ресурсных квот


На мой взгляд, это один из фундаментальных вопросов, напрямую влияющий на безопасностью и работоспособность кластера:


  • любой пользователь принадлежит к группе с четким скоупом прав
  • все запросы разбиваются на категории и обрабатываются с различным приоритетом

Структура лучше хаоса. Концептуально можно сделать так:


  • Бизнес-пользователи смотрят только на витрины данных
  • Аналитики видят стейдж и промежуточные таблицы
  • Инженеры видят сырые данные и метаданные
  • Админ видит их всех :)

Amazon Redshift является форком широко известной PostgreSQL и поэтому синтаксис многим покажется знакомым:


Упрощенная версия конфигурации групп и прав доступа:
----------------------
-- USER MANAGEMENT ---
----------------------

CREATE USER etl WITH PASSWORD '' ;
CREATE USER hevo WITH PASSWORD '' ;

CREATE USER dbt WITH PASSWORD '' ;

CREATE USER da WITH PASSWORD '' ;
CREATE USER nb WITH PASSWORD '' ;

CREATE USER looker WITH PASSWORD '' SYSLOG ACCESS UNRESTRICTED ;

CREATE USER ar WITH PASSWORD '' ;
CREATE USER ak WITH PASSWORD '' ;

-------------------------
--- SCHEMA MANAGEMENT ---
-------------------------

CREATE SCHEMA IF NOT EXISTS hevo AUTHORIZATION hevo ;
CREATE SCHEMA IF NOT EXISTS ext AUTHORIZATION etl ;

CREATE SCHEMA IF NOT EXISTS flatten AUTHORIZATION dbt ;
CREATE SCHEMA IF NOT EXISTS staging AUTHORIZATION dbt ;
CREATE SCHEMA IF NOT EXISTS intermediate AUTHORIZATION dbt ;
CREATE SCHEMA IF NOT EXISTS analytics AUTHORIZATION dbt ;
CREATE SCHEMA IF NOT EXISTS ad_hoc AUTHORIZATION dbt ;
CREATE SCHEMA IF NOT EXISTS meta AUTHORIZATION dbt ;

CREATE SCHEMA IF NOT EXISTS looker_scratch AUTHORIZATION looker ;

-----------------------
-- GROUP MANAGEMENT ---
-----------------------

CREATE GROUP etl
WITH USER etl, hevo ;

CREATE GROUP dbt
WITH USER dbt ;

CREATE GROUP analytics
WITH USER da, nb ;

CREATE GROUP bi
WITH USER looker ;

CREATE GROUP business_users
WITH USER ar, ak ;

----------------------------
-- PRIVILEGES MANAGEMENT ---
----------------------------

-- GROUP etl
GRANT USAGE ON SCHEMA hevo, staging, flatten, analytics, intermediate TO GROUP etl ;

-- GROUP dbt
GRANT USAGE ON SCHEMA hevo, snapshots TO GROUP dbt ;
GRANT USAGE, CREATE ON SCHEMA ext TO GROUP dbt ;

GRANT SELECT ON ALL tables IN SCHEMA hevo TO GROUP dbt ;
GRANT ALL PRIVILEGES ON ALL tables IN SCHEMA meta, ext, snapshots TO GROUP dbt ;

ALTER DEFAULT PRIVILEGES FOR USER hevo IN SCHEMA hevo
   GRANT SELECT ON tables TO GROUP dbt ;

ALTER DEFAULT PRIVILEGES FOR USER etl IN SCHEMA ext
   GRANT ALL PRIVILEGES ON tables TO GROUP dbt ;

-- GROUP analytics
GRANT USAGE ON SCHEMA hevo, ext, flatten, staging, intermediate, restricted, analytics, meta TO GROUP analytics ;
GRANT USAGE, CREATE ON SCHEMA ad_hoc TO GROUP analytics ;

GRANT SELECT ON ALL tables IN SCHEMA hevo, ext, flatten, staging, intermediate, restricted, analytics, meta TO GROUP analytics ;
GRANT ALL PRIVILEGES ON ALL tables IN SCHEMA ad_hoc TO GROUP analytics ;

ALTER DEFAULT PRIVILEGES FOR USER dbt IN SCHEMA flatten, staging, intermediate, restricted, analytics, meta
   GRANT SELECT ON tables TO GROUP analytics ;

ALTER DEFAULT PRIVILEGES FOR USER dbt IN SCHEMA ad_hoc
   GRANT ALL PRIVILEGES ON tables TO GROUP analytics ;

-- GROUP bi
GRANT USAGE ON SCHEMA analytics, meta, ad_hoc, snapshots TO GROUP bi ;

GRANT SELECT ON ALL tables IN SCHEMA analytics, meta, ad_hoc TO GROUP bi ;

ALTER DEFAULT PRIVILEGES FOR USER dbt IN SCHEMA analytics, meta, ad_hoc
   GRANT SELECT ON tables TO GROUP bi ;

-- GROUP business users
GRANT USAGE ON SCHEMA analytics TO GROUP business_users ;
GRANT USAGE, CREATE ON SCHEMA ad_hoc TO GROUP business_users ;

GRANT SELECT ON ALL tables IN SCHEMA analytics TO GROUP business_users ;
GRANT ALL PRIVILEGES ON ALL tables IN SCHEMA ad_hoc TO GROUP business_users ;

ALTER DEFAULT PRIVILEGES FOR USER dbt IN SCHEMA analytics
   GRANT SELECT ON tables TO GROUP business_users ;

У Amazon Redshift очень неплохие настройки automatic workload management (WLM), т.е. автоматическое управление параллелизмом запросов и выделением ресурсов. Но на каком-то моменте и его усилия достигают предела. Redshift просто не может знать всё о специфике вашей нагрузки:


  • Периодичность и время запуска ELT
  • Приоритетные часы для BI и Ad-hoc
  • Какие запросы можно “отстреливать” и почему
  • Наиболее важные запросы, которые точно должны завершаться

Конфигурация очередей (WLM) позволяет гибко управлять нагрузкой на кластер


Конфигурация очередей (WLM) позволяет гибко управлять нагрузкой на кластер


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


Отдельно хочется упомянуть про short-query acceleration. Amazon Redshift гордится использованием ML-алгоритмов для предсказания времени выполнения запросов, и короткие (~ до 30 сек.) пропускает впереди тех, что будут выполняться долго. В любом случае, после включении этой фичи у кластера Wheely как будто открылось второе дыхание, а BI здорово ускорился.


Data Lake ближе чем вы можете представить


Что там с возможностью устроить Даталейк на ровном месте? Она есть! Вместе с расширением Redshift Spectrum кластер приобретает новые возможности:


  • В виде SQL DDL описывать внешние источники данных (файлы в S3)
  • Выполнять запросы к таким данным: доступны проекции (SELECT), фильтры (WHERE), соединения наборов данных (JOINs), вставка (INSERT)
  • Работать с вложенными и полу-структурированными данными: JSON, ORC, PARQUET

Всё это напоминает широко известные EXTERNAL TABLE в _Hive. _И по сути так оно и есть: под капотом используется именно Hive Metastore.


Благодаря Spectrum в Redshift доступен широкий набор популярных форматов данных


Благодаря Spectrum в Redshift доступен широкий набор популярных форматов данных


В Wheely мы нашли несколько применений Spectrum:


1. Data Quality Pipeline


Каждый день при помощи S3 и Spectrum выполняется кросс-сверка операционной базы данных (MongoDB) и Хранилища (Redshift). Чуть подробнее я описывал это в публикации Кто ответит за качество аналитики: QA для Хранилища Данных, и с тех пор даже есть значительные усовершенствования.


2. Архивирование холодных данных в S3


Холодные данные сроком давности >3 лет мы выгружаем в S3 в бинарный колоночный формат parquet. Данные всё так же доступны для запросов пользователей, однако дорогостоящее место в кластере Redshift они уже не занимают, что замечательно.


Пример выгрузки данных для архивирования в S3 (формат файлов parquet)
UNLOAD ('SELECT * FROM "hevo"."events_prod_clickstream_archive"')
 TO 's3://wheely_analytics/dwh/hevo/events_prod/clickstream_archive' 
 IAM_ROLE ''
 MANIFEST
 FORMAT AS PARQUET
 NULL AS ''
 MAXFILESIZE AS 256 
 ENCRYPTED AUTO
 ;

В планах у меня еще одна грандиозная идея: хочется создать полноценный дата-лейк. Но об этом позже.


В случае факапа восстановитесь из бэкапа


Вместо тысячи слов


Вместо тысячи слов


Обычно я не вставляю мемы в публикации, но тут не смог удержаться. Случайно наткнулся и нашел его очень смешным и жизненным. Ситуация может произойти с каждым, и я не исключение. Благо случай не был связан с витринами и production-таблицами, а произошел в рамках одной ad-hoc задачи.


Суть в том, что с недавнего времени появилась возможность восстановить единичные таблицы из бэкапа в указанное целевое место назначения (target table). Очень легко восстановить удаленную или испорченную таблицу из бэкапа за определенную дату. И именно таблицу, а не снапшот целиком. И это не может не радовать. Несколько раз пользовались, и, надеюсь, хватит.


Восстановить удаленную или испорченную таблицу из бэкапа теперь как никогда просто и быстро


Восстановить удаленную или испорченную таблицу из бэкапа теперь как никогда просто и быстро


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


Очень ждём: in-database ML, native semi-structured data support


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


Облизываясь, я смотрю на новые фичи Amazon Redshift, которые уже находятся в статусе preview (пока доступны для тестовых кластеров):



Кажется, я уже придумал им достойное применение. Очень ждём! И, конечно, подготовим свежие обзоры и доклады.


Интересно? Присоединяйся к команде Wheely. Или свяжись со мной и задай вопросы — буду рад ответить.


Следить за моими публикациями в авторском канале: https://t.me/enthusiastech.


Благодарю за внимание.