Перейти к содержимому
Последнее обновление

Колоночное хранение

Кратко Каждая OLAP-таблица Apache Doris записывает данные в колоночные файлы Segment. Для каждого столбца выбирается своё кодирование (PLAIN, dictionary, RLE, bit-shuffle, frame-of-reference), а каждая страница сжимается независимо (по умолчанию LZ4, опционально ZSTD). Постраничные карты зон и фильтры Блума позволяют запросам пропускать страницы без их декодирования, а футер V3, добавленный в 4.1, сокращает время открытия сегментов широких таблиц в 16 раз.

Колоночное хранение Apache Doris: формат файлов сегментов Doris на диске. Кодирование столбцов и сжатие страниц уменьшают размер таблицы; индексы страниц позволяют запросам пропускать страницы, которые не нужно читать.

Зачем использовать колоночное хранение в Apache Doris?​

Apache Doris хранит каждую OLAP-таблицу в колоночных файлах Segment, поскольку такое размещение ограничивает ввод-вывод столбцами, к которым обращается запрос, позволяет выбирать наиболее эффективный кодек для каждого столбца и удобно размещать индексы для пропуска данных. При выполнении аналитических запросов к строчному хранилищу сразу возникают три проблемы.

  • Запрос с GROUP BY country к таблице из 50 столбцов всё равно читает байты остальных 49 столбцов. Затраты на ввод-вывод зависят от ширины строки, а не от числа столбцов в запросе.
  • Слабое сжатие. В соседних строках перемешаны данные разных типов, поэтому словарное кодирование, кодирование длин серий и дельта-кодирование не могут эффективно использовать сходство значений.
  • Сложно пропускать данные. Чтобы определить, что «в этом блоке размером 64 KB нет строк, для которых ts >= '2026-05-01'», нужна статистика по отдельным столбцам, а при строчном размещении для неё нет подходящего места.

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

Что такое колоночное хранение Apache Doris?​

Колоночное хранение Apache Doris представляет собой единый формат файлов сегментов на диске для всех OLAP-таблиц (Duplicate, Unique, Aggregate). Каждая загрузка создаёт один или несколько файлов Segment. Внутри сегмента каждый столбец записывается отдельно: значения потоком поступают на страницы размером ~64 KB, каждая страница кодируется и сжимается, а рядом записываются индексы для пропуска данных. Футер в конце файла содержит указатели на все его части.

Основные термины

  • Segment: единица неизменяемых данных на диске, обозначенная сигнатурой D0R1. Значения по умолчанию: размер страницы 64 KB, размер страницы словаря 256 KB, предельный размер сегмента 256 MB. Определён в be/src/storage/segment/segment.h.
  • Page: часть значений одного столбца. На этом уровне выполняется сжатие и собирается статистика карты зон.
  • Encoding: преобразование отдельного столбца перед сжатием. PLAIN для числовых данных общего назначения, DICT для строк с низкой кардинальностью, RLE для булевых значений, BIT_SHUFFLE для целочисленных столбцов, FOR (frame-of-reference) для дат, BINARY_PLAIN_V2 для строк в V3.
  • Page indexes: индекс порядковых номеров, индекс короткого ключа, карта зон (постраничные min/max и has-null) и фильтр Блума. Все они записываются в сегмент вместе с данными.
  • Footer: запись protobuf в конце файла, описывающая каждый столбец, каждое кодирование, каждый указатель на страницу и расположение каждого индекса. V3 (4.1+) выносит метаданные столбцов за пределы футера, чтобы при открытии широкого сегмента не приходилось читать их целиком.

Как работает колоночное хранение Apache Doris?​

Apache Doris записывает каждый столбец внутри сегмента независимо: кодирует, разбивает на страницы размером ~64 KB, сжимает, индексирует и добавляет футер. Жизненный цикл одного столбца на диске:

  1. Кодирование. Способ кодирования выбирается по типу столбца и распределению значений. Для столбца INT с низкой кардинальностью используется bit-shuffle; для строкового столбца с небольшим числом различных значений создаётся словарь на отдельной странице словаря.
  2. Разбиение на страницы и сжатие. Закодированные значения потоком поступают на страницы размером около 64 KB каждая. По умолчанию каждая страница сжимается с помощью LZ4F либо с помощью ZSTD / Snappy / Zlib, если в таблице задано свойство compression. Сжатие пропускается, если экономия составляет менее 10%.
  3. Индексирование. По мере записи страниц в сегмент также записываются индекс порядковых номеров (двоичный поиск по номеру строки), карта зон для каждой страницы (min, max, has-null) и фильтр Блума для каждого столбца, указанного в bloom_filter_columns. Индекс короткого ключа содержит сводные сведения о префиксе ключа сортировки таблицы.
  4. Футер. В конец файла добавляется SegmentFooterPB, описывающий каждый столбец, каждый указатель на страницу и каждый индекс. В V3 он разделён на две части: компактный футер и область column_meta_entries, которая загружается только для столбцов, к которым действительно обращается запрос.
  5. Чтение. При сканировании открывается футер, круг нужных страниц сужается с помощью индекса порядковых номеров / индекса короткого ключа / карты зон / фильтра Блума, распаковываются только оставшиеся страницы, затем они декодируются, и пакеты данных по столбцам передаются напрямую в векторизованный движок.

Сегмент входит в иерархию Tablet → Rowset → Segment → Column → Page. Компактизация объединяет rowset, но никогда не перезаписывает данные на более мелком уровне; каждый запрос читает данные из лежащей ниже структуры столбцов и страниц.

Быстрый старт​

CREATE TABLE access_log (
ts DATETIME,
user_id BIGINT,
url VARCHAR(512),
country CHAR(2)
)
DUPLICATE KEY(ts, user_id)
DISTRIBUTED BY HASH(user_id) BUCKETS 4
PROPERTIES (
"compression" = "zstd",
"bloom_filter_columns" = "url",
"storage_format" = "V3"
);

SELECT country, COUNT(*) AS hits
FROM access_log
WHERE ts >= '2026-05-01'
GROUP BY country
ORDER BY hits DESC;

Ожидаемый результат

+---------+--------+
| country | hits |
+---------+--------+
| US | 184320 |
| DE | 62018 |
| ... | |
+---------+--------+

Запрос читает только столбцы ts и country. Ещё до распаковки пропускаются страницы, для которых карта зон показывает max(ts) < 2026-05-01. ZSTD обычно уменьшает размер таблицы в 5–10 раз, а футер V3 сохраняет затраты на открытие сегмента на постоянном уровне при росте числа столбцов.

Когда следует использовать колоночное хранение Apache Doris?​

Колоночное хранение Apache Doris является форматом OLAP по умолчанию и единственным доступным форматом, поэтому его использует каждая аналитическая таблица; компромиссы возникают только в особых сценариях, таких как точечные запросы с высоким QPS и настройка кодеков.

Подходящие сценарии

  • Любая аналитическая таблица, с которой вы работаете в Apache Doris. Колоночное хранение обязательно: это формат OLAP по умолчанию и единственный доступный формат.
  • Широкие таблицы и запросы для дашбордов. Чем шире таблица и чем меньше столбцов выбирает запрос, тем больше экономия по сравнению со строчным хранилищем.
  • Таблицы, в которые часто загружаются данные. Сжатие столбцов и постраничные карты зон работают эффективнее по мере накопления строк при неизменной схеме.
  • Широкие таблицы с тысячами столбцов. Установите "storage_format" = "V3" (4.1+), чтобы ограничить время открытия сегментов и потребление памяти.

Неподходящие сценарии

  • Поиск по первичному ключу с SELECT * при тысячах QPS. Каждый столбец читается отдельно, поэтому точечный запрос к широкой строке требует в N раз больше операций ввода-вывода. Используйте механизм высокопараллельных точечных запросов, который добавляет столбец с кэшированными строками с помощью "store_row_column" = "true" в таблицу Unique-Key Merge-on-Write.
  • Настройка compression или storage_page_size без профилирования. Значения по умолчанию (LZ4F, 64 KB) подходят для большинства нагрузок. Переходите на ZSTD, только если стоимость хранения важнее затрат CPU; уменьшайте размер страницы, только если при распаковке небольших столбцов выполняется лишняя работа.
  • Отключение кодирования, чтобы «увидеть столбец в исходном виде». Режима без кодирования нет. Кодек является частью формата файла, а не слоем, который можно убрать.
  • Попытка ускорить точечные запросы с выборкой небольшого числа столбцов в строчной схеме за счёт колоночного хранения. Если каждый запрос имеет вид WHERE id = ? и возвращает одну строку, вам нужна другая система или расширение для строчного хранения, а не другой кодек сжатия.

Производительность / показатели​

  • Футер V3 для широких таблиц: на таблице с 7,000 столбцов и 10,000 сегментов время открытия сегментов сократилось с 65 с до 4 с (в 16 раз быстрее), а потребление памяти при открытии снизилось с 60 GB до менее 1 GB (в 60 раз меньше). Источник: Формат хранения V3.
  • ZSTD в сравнении с прежним вариантом по умолчанию на наборе текстовых журналов объёмом 25 GB / 110 миллионов строк: сжатие почти в 10 раз, что на 53% лучше прежнего варианта по умолчанию, а полный цикл чтения и распаковки выполняется на 30% быстрее. Источник: Примечания к выпуску Apache Doris 1.1.0.

Дополнительные материалы​

  • Формат хранения V3: оптимизация футера для широких таблиц и вынос метаданных отдельных столбцов за его пределы.
  • Отсечение данных: как ZoneMap, BloomFilter и постраничные индексы внутри сегмента позволяют пропускать данные при сканировании.
  • Векторизованное выполнение: движок, которому механизм чтения столбцов передаёт декодированные пакеты данных.
  • Тип данных VARIANT: как пути JSON преобразуются в подстолбцы с тем же кодированием, разбиением на страницы и индексированием, которые описаны здесь.
  • Примечания к выпуску Apache Doris 1.1.0: исходный тест производительности сжатия ZSTD и сведения о его внедрении.