Будучи аналитической СУБД нового поколения, StarRocks на протяжении многих лет пользуется популярностью у широкого круга пользователей за выдающуюся производительность и богатство функций. Параллельно с погоней за функциональностью и скоростью StarRocks постоянно улучшает удобство использования, фокусируясь на рабочих деталях инженеров эксплуатации; особенно начиная с V3.0 сообщество вложило значительные ресурсы в комплексное повышение удобства. Операции по партиционированию и бакетизации, импорту/экспорту, работе с глобальным словарём, наблюдаемости (observability) запросов и т. п. стали значительно проще, благодаря чему удобство использования стало важной продуктовой особенностью StarRocks и продолжает привлекать внимание и признание инженеров эксплуатации. В этой статье кратко показано, как легко работать со StarRocks; мы охватим партиционирование и бакетизацию, импорт данных, преобразование данных и оптимизацию структуры таблиц.

  • Партиционирование и бакетизация: используйте функцию date_trunc() для задания способа партиционирования, при этом нет необходимости думать о ключе бакетизации и количестве бакетов.

  • Импорт данных: при загрузке с HDFS или из облачных хранилищ можно применять более простой и ближе к стандартному SQL синтаксис INSERT FROM FILES.

  • Преобразование данных: благодаря INSERT FROM FILES с поддержкой обычных SELECT и JOIN можно легче и универсальнее выполнять нужные преобразования (Transform), что позволяет уже в процессе импорта удобно выполнять сложную очистку и трансформацию данных.

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

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

Для многих пользователей StarRocks настройки партиционирования и бакетизации становятся первым препятствием; при недостатке понимания одни настраивают «на глаз» (не различая маленькие и большие таблицы и даже игнорируя базовую рекомендацию «1–10 ГБ на один бакет»), другие копируют чей‑то шаблон CREATE TABLE — в итоге производительность запросов оказывается далека от идеала, а чрезмерное число бакетов ведёт к излишнему потреблению памяти метаданными.

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

Рекомендуемый способ партиционирования и бакетизации обычно выглядит так:

CREATE TABLE user_behavior_declared (
    UserID INT(11),
    ItemID INT(11),
    CategoryID INT(11),
    BehaviorType VARCHAR(65533),
    Timestamp DATETIME
)
DUPLICATE KEY (CategoryID, UserID)
PARTITION BY date_trunc('day', Timestamp)
-- DISTRIBUTED BY RANDOM  -- это больше не нужно указывать

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

Что касается бакетизации, после оптимизаций в версиях V3.1 и V3.2 большинство пользователей наконец могут о ней не думать (о ключе и числе бакетов). По умолчанию система применяет RANDOM‑бакетизацию (сейчас поддерживается только для детализированных таблиц) и динамически подстраивает число Tablet в зависимости от информации о кластере, объёма загружаемых данных и способа загрузки. Помимо удобства это существенно снижает потребление памяти и накладные расходы ввода/вывода в сценариях с очень большим количеством Tablet или при близкой к реальному времени загрузке — двойной выигрыш.

Импорт данных: один SQL‑оператор — и вы импортируете терабайты из облачного хранилища

После создания таблицы следующий шаг — импорт данных. Для загрузки больших объёмов традиционно первым выбором был Broker Load, но теперь можно использовать более простой и ближе к обычному SQL синтаксис — INSERT FROM FILES — для импорта данных. Ниже на примере S3 показано, насколько это просто.

Перед импортом обычно выполняют простой SELECT, чтобы посмотреть содержимое данных:

SELECT *
FROM FILES(
    'path' = 's3://starrocks-examples/user_behavior_ten_million_rows.parquet',
    'format' = 'parquet',
    'aws.s3.region' = 'us-east-1',
    'aws.s3.use_instance_profile' = 'false',
    'aws.s3.access_key' = 'AAAAAAAAAAAAAAAAAAAA',
    'aws.s3.secret_key' = 'BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB'
)
LIMIT 5;

С помощью такого простого SELECT вы получите представление о схеме и содержимом (FILES(...) по указанному пути и параметрам автоматически выводит структуру таблицы), что облегчит точное задание схемы таблицы при её создании. Разумеется, можно сразу использовать оператор CTAS для создания временной таблицы и через SHOW CREATE TABLE посмотреть её описание, а затем с учётом своих требований немного поправить и создать итоговую целевую таблицу. Обычно по смыслу бизнес‑данных стоит задать более подходящие и эффективные типы столбцов и способ партиционирования/бакетизации, чтобы добиться более высокой производительности запросов. Одновременно, проверив минимумы и максимумы значений, вы лучше поймёте диапазоны данных.

CREATE TABLE temp_tbl AS
SELECT * FROM FILES(...)
LIMIT 100;

После создания целевой таблицы можно напрямую импортировать данные через INSERT FROM FILES:

INSERT INTO user_behavior_declared
SELECT * FROM FILES(
    'path' = 's3://starrocks-examples/user_behavior_ten_million_rows.parquet',
    'format' = 'parquet',
    'aws.s3.region' = 'us-east-1',
    'aws.s3.use_instance_profile' = 'false',
    'aws.s3.access_key' = 'AAAAAAAAAAAAAAAAAAAA',
    'aws.s3.secret_key' = 'BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB'
);

Во время загрузки вы можете отслеживать прогресс и состояние с помощью SHOW LOAD (или запросом к таблице information_schema.loads). Проверив поле TRACKING SQL, вы получите детальные сведения об ошибках в данных во время импорта, что поможет исправить возможные проблемы в операторе импорта. При загрузке большого количества файлов, особенно если позже выполнялись операции над столбцами и схемы файлов стали не полностью совпадать, INSERT FROM FILES оказывается проще, чем Broker Load. В таких случаях INSERT FROM FILES легче справляется с расхождениями схем между отдельными файлами. Обычно через INSERT FROM FILES можно без проблем загрузить 1 ТБ за один запуск.

Однако данные не всегда тщательно подготовлены, и из‑за проблем качества (например, отсутствующих значений в полях, ошибок преобразования типов и т. п.) импорт может завершиться сбоем всего задания. Поэтому при объёмах от нескольких сотен гигабайт и выше рекомендуется загружать через PIPE. PIPE — это функция, добавленная в StarRocks V3.2 поверх INSERT FROM FILES, предназначенная для массовой пакетной загрузки и для непрерывного импорта данных. При загрузке больших объёмов команда PIPE автоматически разбивает крупное импортное задание на множество мелких задач и выполняет их последовательно. Ошибка в одном файле не приводит к завершению сбоем всего задания импорта. Это снижает стоимость повторных попыток при ошибках и повышает надёжность загрузки.

Кроме того, PIPE может непрерывно отслеживать появление новых файлов или изменения содержимого в облачном каталоге и автоматически разбивать изменившиеся файлы на мелкие задачи импорта, постоянно загружая новые данные в целевую таблицу (для этого в операторе нужно указать 'AUTO_INGEST' = 'TRUE'). Благодаря этому отпадает необходимость поддерживать собственную систему планирования пакетных заданий, что упрощает эксплуатацию импорта данных. Использование также очень простое: достаточно добавить префикс CREATE PIPE к ранее показанному оператору INSERT FROM FILES:

CREATE PIPE user_behavior_pipe
PROPERTIES (
  'AUTO_INGEST' = 'FALSE', -- для непрерывной загрузки установите TRUE
  'BATCH_SIZE'  = '100MB',
  'BATCH_FILES' = '10'
)
AS
INSERT INTO user_behavior_declared
SELECT * FROM FILES (
  'path' = 's3://starrocks-examples/user_behavior_ten_million_rows/*',
  'format' = 'parquet',
  'aws.s3.region' = 'us-east-1',
  'aws.s3.use_instance_profile' = 'false',
  'aws.s3.access_key' = 'AAAAAAAAAAAAAAAAAAAA',
  'aws.s3.secret_key' = 'BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB'
);

В каталоге user_behavior_ten_million_rows/ содержится множество маленьких файлов. Загрузка будет идти пакетами (батчами) по параметру BATCH_FILES, то есть по 10 файлов за партию (как правило, BATCH_SIZE можно не задавать и оставить по умолчанию; при обработке очень больших наборов данных можно настроить размер партии порядка 10 ГБ для поддержания хорошей эффективности). В процессе вы можете смотреть общий прогресс через команду SHOW PIPES, а также детализацию по файлам — в представлении information_schema.pipe_files. PIPE фиксирует статус загрузки каждого файла; по завершении вы можете просмотреть и исправить проблемные файлы и затем повторно загрузить исправленные — это существенно снижает стоимость повторных попыток, вызванных проблемами качества данных.

Преобразование данных: сложную очистку и трансформации можно завершить прямо в процессе импорта

При загрузке через Broker Load можно было с помощью подстановки SET(...) и некоторых скалярных функций генерировать новые столбцы (производные столбцы), но более сложные преобразования, особенно «столбцы в строки», были недоступны. INSERT FROM FILES поддерживает обычные SELECT и JOIN, что позволяет проще и универсальнее реализовывать требуемые преобразования (Transform) и выполнять сложные очистки и трансформации данных ещё в процессе импорта, сокращая последующую дообработку.

Предположим, у вас есть сложный столбец c_arr типа Array, где каждый value — строка, полученная конкатенацией трёх полей через символ #. Пример данных: ["k8s#1#SUCC", "native#2#FAIL"]; три поля в каждом value означают platform, id, status. Вы хотите при импорте развернуть этот столбец Array в несколько строк и в каждой развёрнутой строке добавить три соответствующих поля: platform, id, status — это легко делается с помощью JOIN и UNNEST:

SELECT
  UserID, ItemID, CategoryID, BehaviorType, Timestamp, c_arr,
  split(t_v.unnest, '#')[1] AS platform,
  CAST(split(t_v.unnest, '#')[2] AS INT) AS id,
  split(t_v.unnest, '#')[3] AS status
FROM FILES (
  'path' = 's3://starrocks-examples/user_behavior_ten_million_rows.parquet',
  'format' = 'parquet',
  'aws.s3.region' = 'us-east-1',
  'aws.s3.access_key' = 'AAAAAAAAAAAAAAAAAAAA',
  'aws.s3.secret_key' = 'BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB'
),
UNNEST(c_arr) AS t_v;

Здесь преобразование «столбцы в строки» реализовано через LATERAL JOIN с UNNEST, а функция split разбивает значение на три независимых поля.

Оптимизация структуры таблицы: способ бакетизации можно гибко менять по мере изменения требований

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

Хотя StarRocks умеет автоматически подбирать число бакетов и позволяет указать RANDOM‑бакетизацию, чтобы система по требованию динамически создавала бакеты, вам может показаться, что производительность всё ещё не на пике. В таком случае вы можете хотеть изменить не только будущую бакетизацию, но и распределение для всех или части исторических партиций. В этой ситуации можно использовать ALTER TABLE для настройки структуры и перекладки данных в соответствии с актуальными бизнес‑сценариями и целями по производительности — в том числе переопределить способ бакетизации, число бакетов и ключи сортировки, а при необходимости — менять число бакетов выборочно для части партиций.

Если вас устраивает RANDOM‑бакетизация, вы можете перевести таблицу с хеш‑бакетизацией на RANDOM следующей командой:

ALTER TABLE user_behavior_declared DISTRIBUTED BY RANDOM;

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

ALTER TABLE user_behavior_declared DISTRIBUTED BY HASH(UserID) BUCKETS 12;

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

Если в фильтрах запросов часто используемые условия изменились и прежний ключ сортировки плохо покрывает большинство сценариев, можно изменить ORDER BY так:

ALTER TABLE user_behavior_declared ORDER BY (CategoryID, BehaviorType);

Такие задачи ALTER TABLE могут занять время; отслеживать их прогресс и статус можно через SHOW ALTER TABLE OPTIMIZE.

План по дальнейшему повышению удобства использования

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

  • Реализовать единый и простой синтаксис создания таблиц и развязать зависимость между sort key и table schema для всех типов таблиц.

  • Дальше оптимизировать организацию данных при RANDOM‑бакетизации, по возможности обеспечив отсечение бакетов и для RANDOM‑распределения, чтобы повысить производительность запросов.

  • Сделать INSERT FROM FILES основным унифицированным способом импорта, постепенно расширив поддержку форматов файлов CSV/JSON/Avro/Protobuf и т. д.; добавить явное указание table schema, больше параметров форматов, обёртки для параметров format/credential и другие функции, повышающие удобство.

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

  • Если вам нужен экспорт, уже сейчас поддерживается удобный унифицированный способ — INSERT INTO FILES, а далее будет добавляться поддержка новых форматов.

  • Ещё больше развить функции оптимизации структуры таблиц — чтобы система анализировала типичные паттерны запросов, объём и распределение данных и автоматически выбирала оптимальное распределение данных, повышая удобство партиционирования и бакетизации.

В целом сообщество будет постоянно улучшать удобство использования и рассчитывает на вашу активную практику и обратную связь — давайте вместе создавать мощный и удобный продукт.

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