Тип данных VARIANT
Кратко Тип столбца Apache Doris
VARIANTпредназначен для JSON, структура которого меняется со временем. При записи Apache Doris разбирает каждый документ, определяет тип для каждого пути JSON и сохраняет часто используемые пути как отдельные подстолбцы в колоночном формате. По умолчанию на один столбец VARIANT выделяется до 2048 подстолбцов. Фильтрация и агрегация поv['user_id']затем читают один столбец, не разбирая весь документ. VARIANT получил статус GA в Apache Doris 2.1.0 (март 2024 года); при его разработке использовались идеи Snowflake и ClickHouse.

Зачем использовать тип данных VARIANT в Apache Doris?
Тип данных Apache Doris VARIANT устраняет давний компромисс между гибкостью хранения JSON и скоростью запросов к колоночному хранилищу. Хранение JSON в OLAP-системах долгое время требовало уступок.
- Столбец
STRINGилиJSONBобеспечивает гибкость, но каждый запрос, обращающийся к пути, разбирает весь документ. УсловиеWHERE payload['action'] = 'opened'на миллиарде строк требует прочитать каждый байт всех данных payload, даже еслиactionзанимает лишь одно короткое поле в большом документе. - Статические столбцы работают быстро, но только при стабильной схеме. Когда в следующем спринте источники данных добавят поля, вам снова придётся выполнять миграции через
ALTER TABLEили заводить дополнительный столбец JSON.
Apache Doris VARIANT избавляет от необходимости выбирать. Вы объявляете один столбец, записываете в него JSON, а Apache Doris автоматически переносит часто используемые пути в полноценное колоночное хранилище. Запросы по-прежнему пишутся на SQL, а на диске таблица во многом напоминает ту, которую вы спроектировали бы вручную.
Что представляет собой тип данных Apache Doris VARIANT?
Apache Doris VARIANT представляет собой тип столбца для полуструктурированных данных. VARIANT принимает любое допустимое значение JSON: скаляры, массивы и вложенные объекты. По мере поступления строк механизм записи строит префиксное дерево путей, определяет тип для каждого пути и записывает каждый путь в отдельный столбец с постраничным кодированием и индексированием внутри сегмента. Вы запрашиваете JSON, а на диске каждому пути соответствует отдельный подстолбец.
Основные термины
Subcolumn: независимый столбец на диске, сформированный из пути JSON. Каждый подстолбец имеет собственное кодирование, сжатие, zone map и bloom filter, как и статический столбец.Subcolumnization: процесс выделения часто используемых путей JSON в подстолбцы при записи. Управляется параметромvariant_max_subcolumns_count, значение по умолчанию:2048.Schema Template: необязательная конструкцияVARIANT<'path' : TYPE, ...>, которая закрепляет за выбранными путями фиксированный тип, чтобы структура хранения и индексы оставались стабильными при последующих загрузках.Sparse column: общее резервное хранилище, в которое попадают редко встречающиеся пути, когда число путей превышает лимит подстолбцов. Множество логических путей размещается в одном физическом столбце или в нескольких столбцах, если включено шардирование разреженных данных.Type promotion: правило, определяющее, как поступать, если один и тот же путь в одной строке имеет типINT, а в другой типSTRING. Целые числа разной разрядности приводятся к более широкому типу (отTINYINTдоBIGINT); при других конфликтах, например между целыми числами и числами двойной точности или между числами и строками, используетсяJSONB.
Как работает тип данных Apache Doris VARIANT?
Apache Doris VARIANT разбирает каждый документ JSON при записи, определяет тип для каждого пути JSON и сохраняет часто встречающиеся пути как независимые подстолбцы в колоночном формате, а редко встречающиеся пути помещает в общий разреженный столбец. Жизненный цикл одного пути на диске выглядит так:
- Разбор и определение типа. Когда JSON поступает в memtable, механизм записи добавляет каждый конечный путь в префиксное дерево и выбирает для каждого пути один тип хранения:
BIGINTдля целых чисел,DOUBLEдля дробных чисел,STRING,BOOLEANилиARRAY<T>для массивов скалярных значений. Вложенные объекты преобразуются в пути большей глубины. - Расширение типа или переход к резервному типу. Если новое значение конфликтует с текущим типом, Doris по возможности расширяет тип (целые числа разной разрядности приводятся к
BIGINT). В остальных случаях, например при сочетании целых чисел с числами двойной точности или строками, путь переводится вJSONB, и запросы теряют возможность pushdown для этого пути до исправления схемы. - Запись в подстолбцы или разреженный столбец. Пути с наибольшей долей значений, отличных от NULL, становятся независимыми подстолбцами, которые кодируются и сжимаются точно так же, как статические столбцы. По достижении
variant_max_subcolumns_countоставшиеся редко встречающиеся пути упаковываются в разреженный столбец. - Объединение схем. Каждый rowset хранит собственную схему подстолбцов. Компактация объединяет rowset с использованием наименьшей общей схемы столбцов, поэтому добавление нового поля сегодня не требует выполнения
ALTER TABLE. - Чтение по пути.
SELECT v['user_id']обращается к одному подстолбцу и читает только его, пропуская страницы с помощью zone map и bloom filter так же, как при работе со статическими столбцами. Работает pushdown предикатов:CAST(v['user_id'] AS BIGINT) = 42выполняется над типизированным подстолбцом, а не над текстом JSON.
Инвертированный индекс работает с подпутями аналогичным образом. INDEX idx_v(v) USING INVERTED PROPERTIES("parser" = "english") позволяет искать по каждому текстовому подстолбцу, а Schema Template позволяет привязать индекс к одному конкретному пути.
Быстрый старт
CREATE TABLE github_events (
id BIGINT,
type VARCHAR(30),
payload VARIANT,
created_at DATETIME,
INDEX idx_payload (payload) USING INVERTED PROPERTIES("parser" = "english")
)
DUPLICATE KEY(id)
DISTRIBUTED BY HASH(id) BUCKETS 4
PROPERTIES ("storage_format" = "V3", "replication_num" = "1");
-- INSERT keeps a plain string as a VARIANT string, so parse the JSON text.
-- Load jobs such as Stream Load parse JSON automatically.
INSERT INTO github_events VALUES
(1, 'PushEvent', PARSE_TO_VARIANT('{"action":"opened","commits":[{"sha":"abc"}],"size":4}'), '2026-05-01'),
(2, 'IssueCommentEvent', PARSE_TO_VARIANT('{"action":"created","issue":{"number":42}}'), '2026-05-01');
SELECT CAST(payload['action'] AS STRING) AS action, COUNT(*) AS n
FROM github_events
WHERE payload['action'] MATCH 'opened'
GROUP BY action;
Ожидаемый результат
+--------+---+
| action | n |
+--------+---+
| opened | 1 |
+--------+---+
Фильтр читает один подстолбец, использует инвертированный индекс и не обращается к массиву commits. Выполните SET describe_extend_variant_column = true; DESC github_events;, чтобы увидеть автоматически определённые подстолбцы (payload.action, payload.commits, payload.issue.number, ...) так, словно вы объявили их вручную.
Когда стоит использовать тип данных Apache Doris VARIANT?
Используйте Apache Doris VARIANT, когда схема JSON меняется, но большинство запросов обращается к небольшому числу привычных путей. Если структура фиксирована, выбирайте статические столбцы или STRUCT, а для первичных ключей, ключей сортировки и соединения используйте типизированные столбцы.
Подходящие сценарии
- Журналы событий и данные аудита, где большинство запросов обращается к нескольким привычным путям, а остальная часть документа просто хранится вместе с ними.
- Таблицы телеметрии и профилей пользователей, в которых источники данных постоянно добавляют поля, а приём данных нельзя останавливать ради миграции схемы.
- Данные наблюдаемости с тысячами необязательных тегов. Часто используемые теги становятся подстолбцами, а редко встречающиеся попадают в разреженный столбец, не увеличивая объём работы при компактации.
- Полнотекстовый поиск внутри JSON за счёт сочетания
VARIANTсUSING INVERTED.
Неподходящие сценарии
- Таблицы со стабильной, хорошо понятной схемой. Обычные типизированные столбцы проще понимать и индексировать, и они не могут неожиданно перейти к типу
JSONB. ИспользуйтеVARIANT, только когда схема действительно меняется. - Первичные ключи, ключи сортировки или соединения. Столбец
VARIANTне может быть ключевым столбцом. Значения VARIANT можно использовать при соединении и сортировке, но это медленнее, чем работа со статическими столбцами. Еслиidслужит ключом соединения, объявите его как статический столбецBIGINT. - Поиск по всему документу, например
WHERE v LIKE '%doris%'. Инвертированный индекс строится для подпутей, а не для всего столбца. Храните исходный JSON в дополнительном столбцеSTRINGи индексируйте его либо включите режим DOC для быстрого выполненияSELECT v. - Фиксированная структура, известная уже на этапе проектирования. Если каждая строка содержит ровно
{a, b, c}, а типы никогда не меняются,STRUCTзадаёт более строгие правила: он запрещает непредусмотренные поля, тогда какVARIANTпринимает их без предупреждений. - Очень широкий JSON (10k+ путей) без настройки. По умолчанию число путей, выделяемых в подстолбцы, ограничено 2048. При большем числе путей планируйте использование разреженных столбцов или режима DOC и прочитайте руководство по рабочим нагрузкам VARIANT перед загрузкой данных в рабочую среду.
Производительность / показатели
- ClickBench, 43 запроса на экземпляре EC2 с 16 ядрами / 64 GB памяти: VARIANT занимал 12.7 GB против 35.7 GB для JSON (на ~65% меньше). При прогретом кэше запросы выполнялись примерно в 8 раз быстрее, чем при хранении в JSON, а расхождение с показателями заранее заданных статических столбцов укладывалось в ~10%. Источник: Variant в Apache Doris 2.1.0.
- GuanceDB при переносе данных наблюдаемости из Elasticsearch в Doris с использованием VARIANT: затраты на вычислительные ресурсы снизились на 70%, общая скорость запросов выросла в ~2 раза, простые запросы ускорились в 4 раза и более. Источник: та же публикация.
Дополнительные материалы
- Справочник SQL по VARIANT: полный синтаксис, правила типов, индексы, настройки
- Руководство по рабочим нагрузкам VARIANT: когда выбирать стандартный режим, разреженное хранение, режим DOC или Schema Template
- Импорт данных VARIANT: примеры Stream Load, Routine Load и Broker Load
- Карточка функции «Колоночное хранение»: как устроены кодирование, разбиение на страницы и индексирование подстолбцов
- Обзор инвертированного индекса: индекс, который VARIANT использует для поиска текста в JSON
- SQL-функции LLM: семейство
AI_*, результаты которого в формате JSON (отAI_EXTRACT,AI_CLASSIFYи других функций) удобно хранить в столбце VARIANT. - Блог Apache Doris: Variant в Apache Doris 2.1.0, первоначальный анонс и результаты тестов производительности