Привет, Хабр!
На связи Артемий Козырь из команды Аналитики, и я продолжаю знакомить вас с Wheely. В этом выпуске:
- Основы гибких кластерных вычислений
- Колоночное хранение и компрессия данных
- Вместо индексов: ключи сегментации и сортировки
- Управление доступами, правами, ресурсами
- Интеграция с S3 или Даталейк на ровном месте
Все аналитические решения Wheely так или иначе строятся на основе Хранилища Данных, движком которого выступает продукт с кодовым названием Redshift от провайдера облачных сервисов Amazon. Но почему именно этот продукт?
Эту публикацию я бы охарактеризовал как базовую и основополагающую. Сложные механизмы и принципы работы системы скрыты от бизнес-пользователей, но именно они лежат в основе производительного и современного решения. Постараюсь внести ясность и подчеркнуть для вас ключевые особенности.
Кластерные вычисления как основа архитектуры
Кластер состоит из leader node (координатор) и набора compute node (вычислительный узел). Координатор играет роль связующего звена между кластером и пользовательскими приложениями, генерирует планы запросов, управляет коммуникацией. Вычислительные узлы — рабочие лошадки, на которых выполняется основная часть расчетов.
Redshift обеспечивает параллельную обработку данных на кластере машин
Этот принцип широко известен как MPP (massively parallel processing, массово-параллельная архитектура). Не лишним будет упомянуть и другие популярные СУБД, использующие подобные принципы: BigQuery, Vertica, Greenplum, Teradata, Azure SQL DW.
Каждая compute node обладает набором выделенных ресурсов: CPU, memory, disk, которые определяются типом виртуальной машины.
Спецификация типов compute node доступных для Amazon Redshift
Сегодня в Wheely мы используем 8 узлов типа dc2.large, что в сумме составляет 16 vCPU, 120 GiB памяти, 1.3 TB объем диска. Кто-то может справедливо заметить, что это не такие уж и огромные объемы данных, на что я бы возразил: суть не в объеме, а в том, какие результаты и пользу вы можете выжать из этих данных.
Не могут не радовать элементы гибкости и эластичности (elasticity), в целом присущие облачным сервисам. С ростом бизнеса и потребности в аналитике несколько раз без особого труда производилось расширение кластера: увеличивалось количество узлов (resize), осуществлялся переход на новое поколение виртуальных машин (node upgrade).
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
Однако при должном усердии можно добиться еще более значительного сжатия, чем дефолтные кодеки, выбранные Amazon Redshift. Чуть подробнее этот вопрос я раскрывал в публикации Compressing Redshift columnar data even further with proper encodings.
Пересмотр алгоритмов сжатия данных позволил сэкономить 21% дискового пространства
Ключи сегментации и сортировки вместо индексов
В Redshift вторичные поисковые структуры данных (индексы) в привычном понимании (B-Tree, Bitmap) отсутствуют. Здесь они просто не нужны, и вот почему:
Тонкая настройка позволяет задать принцип, по которому строки будут распределены между узлами кластера: равномерное (EVEN), копия на каждом (ALL), или по ключу (KEY). Эта конфигурация носит название тип сегментации (distribution style).
3 типа распределения каждой строки данных в кластере: KEY, ALL, EVEN
А уже данные в блоках на каждом узле можно физически хранить в заданном порядке, то есть отсортированными. На ум приходит сортировка, например, по монотонно возрастающему идентификатору. Конфигурация, использующая один и тот же атрибут в distribution key, sort key обеспечит использование самого оптимального типа соединения таблиц — SORT MERGE JOIN:
{{
config(
dist='request_id',
sort='request_id'
)
}}
Смотрите на это как на конструкцию ORDER BY в SQL-запросе, сохраняющую порядок в таблице на диске. При этом задействованы могут быть несколько колонок — тогда это уже составной ключ (compound sort key).
{{
config(
materialized='table',
dist="city",
sort=['city', 'date', 'product_name']
)
}}
В дополнение, есть еще такой очень хитрый вид сортировки как Interleaved sort key, который дает одинаковый вес любой из колонок (или их комбинации) в ключе сортировки. Он отлично подходит к витринам данных, для которых нет одного заранее известного паттерна доступа.
{{
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) позволяет гибко управлять нагрузкой на кластер
Однако это уже тема отдельной публикации, и потенциально я могу сделать подробный доклад по разграничению прав доступа и тонкой настройки очередей.
Отдельно хочется упомянуть про 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 доступен широкий набор популярных форматов данных
В Wheely мы нашли несколько применений Spectrum:
1. Data Quality Pipeline
Каждый день при помощи S3 и Spectrum выполняется кросс-сверка операционной базы данных (MongoDB) и Хранилища (Redshift). Чуть подробнее я описывал это в публикации Кто ответит за качество аналитики: QA для Хранилища Данных, и с тех пор даже есть значительные усовершенствования.
2. Архивирование холодных данных в S3
Холодные данные сроком давности >3 лет мы выгружаем в S3 в бинарный колоночный формат parquet. Данные всё так же доступны для запросов пользователей, однако дорогостоящее место в кластере Redshift они уже не занимают, что замечательно.
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 (пока доступны для тестовых кластеров):
- Using machine learning in Amazon Redshift
- Ingesting and querying semistructured data in Amazon Redshift
Кажется, я уже придумал им достойное применение. Очень ждём! И, конечно, подготовим свежие обзоры и доклады.
Интересно? Присоединяйся к команде Wheely. Или свяжись со мной и задай вопросы — буду рад ответить.
Следить за моими публикациями в авторском канале: https://t.me/enthusiastech.
Благодарю за внимание.
StraNNikk
Это конечно все здорово, вот только у Redshift есть на мой взгляд супер-большой минус, который ставит на этой БД крест по сравнению с аналогами — необоснованно долгое время компиляции запроса. То есть даже простой запрос при первом запуске может выполняться 4-5 секунд, что ИМХО ну совсем несерьезно. Вот тут кстати подробно описано: medium.com/@pingram/redshift-code-compilation-977143576e89. На моём проекте вся инфраструктура на амазоне и казалось логичным взять RedShift для аналитики, но в итоге решили использовать Vertica — ни о чем не жалею.
kzzzr Автор
Верно! Есть такая особенность, может показаться неприятной.
Не буду оправдывать или защищать, Vertica я тоже очень люблю.
Что по поводу бюджета? При примерно одинаковых мощностях, что выходит дороже?
Как решается вопрос с Operations? Обновление версий/maintenance? На себя всё берет вендор?
StraNNikk
При прочих равных Vertica будет дороже, но я серверами не занимаюсь и деталей не скажу. Плюс само собой поддержка сложнее — редшифт из коробки одной кнопкой, а кластер вертики нужно разворачивать + это должно быть минимум 3 ноды для обеспечения кворума.
Но с другой стороны Vertica как мне кажется в функциональном плане намного лучше (тут оговорюсь, что я смотрю со своей программистской колокольни). Вот взять хотя бы драйвер для питона — у редшифта драйвер появился только полгода назад, а до этого для коннекта предлагали использовать постгрешный psycopg2. Всякие ошибки и прочие вещи для Vertica гуглятся проще, ну и в целом довольно большое комьюнити.
kzzzr Автор
Спасибо за ответ. Примерно так и предполагал. У нас сейчас в команде нет возможности выделить ресурс на полноценный Ops-сопровождение решения.
В Redshift радует то, что это fully managed service. Раз в неделю автоматические обновления, в случае необходимости рестарт кластера 1-й кнопкой, автоматические бэкапы, и всё родное для AWS.
Vertica функциональнее, соглашусь. Если уметь выжать из неё всё. Уже несколько лет назад там были полнотекстовый индекс, in-database ML, сессионизация, pattern-matching по сессиям. И фишка с проекциями конечно супер.
kzzzr Автор
Кстати, справедливости ради, хочется именно подчеркнуть, что время на компиляцию требуется только при первом запуске запроса. Все последующие запуски в точности того же запроса происходят без этапа компиляции.
Т.е. при использовании BI-инструментов (читай конструкторов запросов) и преднастроенных дашбордов эффект может быть и незаметен.
StraNNikk
Ну есть кейсы, когда допустим базой пользуются ребята из отдела аналитики. И пользуются они редко, и тем более из консоли, да и вообще куда им спешить — могут и подождать.
В моем кейсе база нужна, чтобы строить графики/таблицы для пользователей-кастомеров, и тормозить каждый HTTP-запрос веб-сервера на 4-5 секунд как-то совсем не комильфо. Каждая колонка в таблице — это фильтр в интерфейсе. Фильтров много, иногда делаются джойны, параметры запросов все разные. «Прогреть кэш» в данном случае не видится возможным