Learning Platform
Глоссарий Troubleshooting
Урок 16.04 · 40 мин
Продвинутый
VortexSpiralDBExtensible EncodingCascading CompressionLayout TreeFastLanesALPFSSTBtrBlocksDuckDB ExtensionLinux Foundation

Vortex Architecture

Если Lance атакует Parquet со стороны ML workload’ов (random access, vector search), то Vortex атакует Parquet в его родной области — аналитических scan’ах. Vortex предлагает: Parquet-уровень компрессии, 10-20x быстрее сканирование, и главное — compute на сжатых данных без декомпрессии.

Проект создан компанией SpiralDB, передан в Linux Foundation AI & Data в августе 2025. Backers: Microsoft, Snowflake, Palantir, NVIDIA. Файловый формат стабилен с v0.36.0. С января 2026 — core extension в DuckDB (не community, а official).

NOTE

Vortex — это Rust-реализация, без нативного Python SDK. Основной способ работы — через DuckDB extension: INSTALL vortex; LOAD vortex; SELECT * FROM read_vortex('file.vortex');. Это принципиальное решение: вместо Python-wrapper’а — интеграция с query engine, который понимает compressed compute.

Проблема: декомпрессия как bottleneck

Parquet компрессирует данные (Snappy, ZSTD, LZ4) и декомпрессирует при чтении. Для каждого запроса pipeline выглядит так:

Parquet Read Path: decode → decompress → compute
SELECT SUM(amount) WHERE status = 'shipped'SQL запрос: SELECT SUM(amount) FROM orders WHERE status = 'shipped'. Parquet reader: загрузить column chunks, декодировать encodings, декомпрессировать блоки, применить предикат, агрегировать.
1. Read compressedЗагрузить compressed column chunks с диска / S3. I/O: пропорционален compressed size (хорошо — сжатие уменьшает I/O).
2. DecompressZSTD/Snappy/LZ4 декомпрессия: CPU-intensive операция. Для 1GB compressed → ~3GB decompressed. CPU time: ~500ms на одном ядре. ZSTD L3: ~800 MB/s decompress throughput.
3. Decode encodingsRLE, DICTIONARY, DELTA — decode Parquet encodings в raw values. Ещё один CPU pass: для dictionary encoding каждое значение = lookup в dictionary. Для RLE: развернуть повторы.
4. ComputeНаконец: применить WHERE status = 'shipped' (scan decompressed column), вычислить SUM(amount) (scan другой decompressed column). CPU работает на полностью распакованных данных.

Vortex устраняет шаги 2 и 3: compute выполняется прямо на сжатых данных. Вместо decompress → compute — compressed compute:

Vortex: compressed compute (без декомпрессии)
SELECT SUM(amount) WHERE status = 'shipped'Тот же запрос. Vortex reader: загрузить encoded data, выполнить filter и aggregate напрямую на encoded representation. Нет step декомпрессии.
1. Read encodedЗагрузить encoded (не compressed!) данные. Vortex encodings — не compression в классическом смысле. Это structured representations, которые engine может интерпретировать напрямую.
2. Compressed ComputeFilter: status = 'shipped'. Если status закодирован dictionary encoding, то filter = dictionary_lookup('shipped') → index → bitmap scan. Нет развёртывания всей колонки. SUM(amount): если amount закодирован как base + delta, SUM = N*base + SUM(deltas). Compute на encoding representation.

Результат: 5-10x быстрее Меньше memory footprint

Результат: тот же ответ, но CPU time: ~50-100ms вместо ~500ms. 5-10x ускорение за счёт отсутствия decompress + decode steps. Memory: compressed representation вместо 3GB decompressed.

Extensible Encodings

Ключевой архитектурный принцип Vortex: кодировки — first-class расширяемые объекты, а не фиксированный набор:

Extensible Encodings: Parquet vs Vortex

Parquet: фиксированные

Parquet: кодировки зафиксированы в спецификации (Thrift). Каждая новая кодировка = изменение Thrift spec + голосование Apache + обновление всех реализаций (Java, C++, Rust, Python). Цикл: 6-18 месяцев.
НаборPLAIN, RLE_DICTIONARY, DELTA_BINARY_PACKED, DELTA_BYTE_ARRAY, DELTA_LENGTH_BYTE_ARRAY, BYTE_STREAM_SPLIT, RLE. ~10 кодировок за 12 лет существования формата. BtrBlocks, FastLanes, ALP — исследования 2022-2024, но в Parquet их нет.

Vortex: pluggable

Vortex: кодировки — pluggable модули. Каждая кодировка = Rust trait implementation + FlatBuffer descriptor. Новая кодировка = новый crate. Нет изменения спецификации формата. Self-describing: reader декодирует WASM decoder из файла.
НаборBuilt-in: ALP (float), FastLanes (int), FSST (string), Dictionary, RLE, BitPacked, Delta, Roaring (bitmap), Constant, Sparse. Custom: любая кодировка через Rust trait. WASM decoder для forward compatibility.

Каталог встроенных кодировок

Vortex включает реализации новейших исследований в области кодирования данных:

Встроенные кодировки Vortex
КодировкаНазвание кодировки и тип данных, для которого она предназначена.
Тип данныхКакие типы данных кодируются этим encoding'ом.
ОписаниеПринцип работы и преимущества.
FastLanesVLDB 2023. SIMD-friendly integer encoding. Данные переупорядочиваются (lane layout) так, что bitunpack/delta/FOR операции — один SIMD instruction per batch. x86 AVX-512, ARM NEON. 10-50x быстрее scalar bitunpacking.
Целые числа: int8-int64, timestamps, date. Любые данные, которые можно представить как integer sequence.
SIMD-friendly переупорядочивание: данные расставлены в lane layout для vectorized bitunpack. Один AVX-512 instruction обрабатывает 16 int32 за такт. Decode throughput: 10-50 GB/s.
ALPSIGMOD 2024. Adaptive Lossless floating-Point compression. Exploits: реальные float данные имеют ограниченное количество значащих десятичных цифр. float64 с 2 десятичными знаками (цена: 19.99) → 2 байта вместо 8.
Floating-point: float32, float64. Оптимизирован для 'реальных' данных: цены, координаты, метрики — где значащих цифр мало.
Decimal digits extraction: 19.99 → integer 1999 + exponent 10^-2. Integers сжимаются bitpacking/FastLanes. 2-4x лучше BYTE_STREAM_SPLIT (Parquet).
FSSTVLDB 2020. Fast Static Symbol Table compression для строк. Строит таблицу символов (до 255 entries) из corpus, заменяет частые подстроки на 1-byte codes. 5-10x compression, 3-5 GB/s decode.
Строки: UTF-8, URLs, JSON paths, log messages. Любые повторяющиеся текстовые паттерны.
Symbol table: частые подстроки → 1-byte codes. 'https://api.' → 0x42. Decode: lookup table, cache-friendly. 5-10x compression ratio.
DictionaryКлассическая dictionary encoding. Уникальные значения в словаре, данные хранятся как indices. Для low-cardinality колонок: enum, status, category. Vortex: dictionary может быть encoded дальше (cascading).
Low-cardinality: enum, status, boolean strings, category. Эффективен когда unique values << total rows.
Значения → indices в словаре. Indices → bitpacked/FastLanes дальше (cascading). Словарь: sorted для binary search predicate pushdown.
RLERun-Length Encoding: consecutive repeats → (value, count). Для sorted колонок: date partitions, region codes. Vortex RLE supports compressed compute: SUM over RLE = Σ(value × count).
Sorted/grouped data с длинными повторами: partitioned columns, event types в sorted log.
Repeats → (value, count). Compressed compute: COUNT = Σ(counts), SUM = Σ(value×count). Нет развёртывания при агрегации.
BitPackedBit-packing: значения с ограниченным range хранятся в минимальном количестве бит. Значения 0-15 → 4 bits каждое (вместо 32). Frame-of-Reference: base + packed deltas.
Integers с известным range: ages (0-120 → 7 bits), counts (0-1000 → 10 bits), encoded indices.
N-bit packing: значения 0-15 → 4 bits. 32-bit int → 4-bit packed = 8x compression. SIMD-friendly: FastLanes bitunpack.

Layout Tree

Vortex организует данные в layout tree — дерево из трёх базовых типов layouts, которые компонуются рекурсивно:

Layout Tree: три базовых layout'а

Layout Tree (рекурсивное дерево)

Layout tree — рекурсивная структура, описывающая физическую организацию данных в Vortex файле. Каждый узел — один из трёх типов: Struct (колоночное разбиение), Chunked (строковое разбиение), Flat (leaf с данными).
Struct LayoutColumnar partitioning: разбивает запись на колонки. Каждый child — layout одной колонки. Аналог Parquet row group: одна запись = набор колонок. Struct может содержать nested Struct (для struct types в Arrow).
Chunked LayoutRow-wise partitioning: разбивает данные на chunks (по N строк). Каждый child — layout одного chunk'а. Аналог row groups в Parquet. Обеспечивает streaming read + per-chunk statistics.
Flat LayoutLeaf node: содержит encoded данные одной колонки одного chunk'а. Encoding + compressed bytes. Flat layout — единственный тип, содержащий реальные данные. Struct и Chunked — только структура.

Пример layout tree для таблицы с 3 колонками и 2 chunks:

Пример: Layout Tree для orders table

Chunked (root)

Root: Chunked layout. Данные разбиты на 2 chunks по ~1M строк. Каждый chunk обрабатывается независимо: streaming read, per-chunk predicate pushdown, parallel scan.
Chunk 0Первый chunk: ~1M строк. Внутри — Struct layout, разбивающий chunk на колонки.
order_idFlat: int64, FastLanes encoding. Монотонно растущие ID → delta + FastLanes bitunpack. Compression: ~2 байта/значение (вместо 8).
amountFlat: float64, ALP encoding. Цены с 2 десятичными знаками → integer extraction + bitpack. Compression: ~3 байта/значение (вместо 8).
statusFlat: string, Dictionary + FSST. 5 уникальных статусов → 3-bit indices. FSST на dictionary values для дополнительного сжатия.
Chunk 1Второй chunk: те же колонки, но encoding может отличаться (adaptive per-chunk). Если в chunk 1 distribution данных другой — другой encoding.
order_idДругой chunk — возможно другой encoding. Если ID не монотонные (пропуски, параллельные writer'ы) — plain + bitpack вместо delta.
amountТе же цены — тот же ALP encoding. Adaptive choice подтверждает: ALP оптимален для float с ограниченной точностью.
statusЕсли в chunk 1 только 'shipped' (99%) — RLE эффективнее dictionary. Adaptive choice: RLE. Compressed compute: COUNT WHERE status='shipped' = RLE run length.

Cascading Compression

Vortex применяет каскадную компрессию — encodings вкладываются друг в друга:

Cascading Compression: вложенные encodings

String column: 1M строк, 5 unique

Исходные данные: string колонка 'status' с 5 уникальными значениями в 1M строк. Задача: сжать максимально, сохранив возможность compressed compute.
Step 1: Dictionary
Dictionary Encoding5 уникальных строк → dictionary (5 entries). Данные: 1M × index (0-4). Уже compression: string (avg 8 bytes) → index (3 bits). Но indices хранятся как int32 (4 bytes) — не оптимально.
Step 2: BitPack indices
BitPacked EncodingIndices 0-4 → 3 bits each. BitPack: 1M × 3 bits = 375KB (вместо 4MB). 10x compression поверх dictionary. Каскад: Dictionary → BitPacked.
Step 3: FastLanes pack
FastLanes EncodingBitPacked данные переупорядочиваются в FastLanes lane layout для SIMD decode. При scan: один AVX-512 instruction распаковывает 16 значений за такт. Каскад: Dictionary → BitPacked → FastLanes.

8MB → ~375KB (21x compression) SIMD decode: FastLanes unpack Compressed filter: O(dict_size)

Итого: string column 1M × avg 8 bytes = ~8MB → Dictionary → BitPack → FastLanes = ~375KB + 40 bytes dict. Compression ratio: ~21x. Decode: SIMD FastLanes unpack → index → dictionary lookup. Compressed compute: filter status='shipped' → dict_lookup('shipped')=2 → bitmap scan на BitPacked.
TIP

Каскадная компрессия — ключевое отличие от Parquet, где dictionary encoding и RLE — “плоские”, не вложенные. В Parquet dictionary-encoded колонка хранит indices как RLE-encoded int32 — два уровня. Vortex допускает произвольную глубину: Dictionary → BitPack → FastLanes → lane permutation. Каждый уровень добавляет сжатие или ускоряет decode.

BtrBlocks-style Codec Selection

Vortex использует подход, вдохновлённый BtrBlocks (SIGMOD 2023): автоматический выбор оптимального каскада кодировок для каждого chunk’а:

Codec Selection Pipeline

Chunk: 64K строк

Входной chunk: ~64K строк одной колонки. Writer анализирует данные перед выбором encoding cascade. Анализ: тип данных, null ratio, cardinality, value range, sortedness, repetition patterns.
sample + analyze
Data ProfileПрофиль данных: type=float64, null_ratio=0.01, cardinality=0.95 (high), range=[0.01, 9999.99], decimal_digits=2, sorted=false. Профиль определяет выбор encoding cascade.
select cascade
Float, 2 decimalsfloat64 с 2 десятичными → ALP: extraction 19.99 → 1999 (int16). Затем BitPack на integers. Compressed compute: SUM = SUM(extracted) × 10^-2.
Int64, sortedSorted int64 (timestamps, IDs) → Delta encoding: store differences. Deltas обычно маленькие → BitPack. Затем FastLanes для SIMD decode.
String, low cardLow-cardinality strings → Dictionary. Indices → BitPack. BitPacked indices → FastLanes для SIMD unpack. Dictionary values → FSST для сжатия самого словаря.

File Structure

Vortex файл организован как FlatBuffer-описанная структура:

Vortex File Structure

Vortex File (.vortex)

Vortex file: self-describing бинарный формат. FlatBuffer metadata (не Thrift как Parquet, не protobuf как Lance). Структура: data sections → layout tree → footer. Чтение: footer → layout tree → выборочные data sections.
Data SectionsEncoded data для каждого Flat leaf в layout tree. Порядок: depth-first traversal layout tree. Каждая секция: raw encoded bytes (FastLanes packed, ALP encoded, etc.). Нет заголовков — вся metadata в layout tree.
Layout TreeFlatBuffer-сериализованное дерево layout'ов. Каждый узел: тип (Struct/Chunked/Flat), children offsets, encoding descriptor для Flat nodes, statistics (min/max/null_count). Полное описание структуры файла.
FooterФиксированный размер: magic bytes ('VRTX'), version, offset к layout tree section, schema (Arrow schema serialized). Reader: read footer → layout tree → selective data read.

WASM Decoders

Для forward compatibility Vortex встраивает WASM decoders в файлы:

WASM Decoder: self-describing файлы

Без WASM: reader must know all

Традиционный подход: reader обязан знать все encodings. Если файл использует encoding v2.0, а reader — v1.0, файл нечитаем. Решение: обновить reader. Проблема: файлы-сироты, reader fragmentation.
ПроблемаReader v1 не знает FastLanes-v2 → файл нечитаем. Нужно обновить reader. Для архивных данных (10-year retention) — reader может не существовать через 10 лет.

С WASM: decoder в файле

Vortex WASM: файл содержит WASM-модули для декодирования своих encodings. Reader v1 встречает unknown encoding → загружает WASM decoder из файла → выполняет в sandbox → данные декодированы. Self-describing.
РешениеФайл самодостаточен: содержит данные + декодеры. Любой Vortex reader может прочитать любой Vortex файл, даже с unknown encodings. 10-year retention: decoder всегда с данными.
WARNING

WASM decoders — forward-looking feature. В текущей версии (v0.36+) WASM decoders описаны в спецификации, но практическое использование ограничено built-in encodings. Основная ценность — для долгосрочного хранения: файлы, записанные сегодня, будут читаемы через 10 лет без обновления reader’а.

DuckDB Integration

С января 2026 Vortex — core extension в DuckDB:

DuckDB + Vortex: query pipeline
SELECT region, SUM(revenue) FROM read_vortex('data.vortex') WHERE year = 2025 GROUP BY regionSQL запрос в DuckDB. Vortex extension перехватывает read_vortex() → использует layout tree для predicate pushdown → compressed compute где возможно → Arrow output.
1. Layout Tree ReadDuckDB загружает footer + layout tree. Из layout tree: schema, chunk boundaries, per-chunk statistics. Statistics: year column min/max per chunk → skip chunks where max(year) < 2025 or min(year) > 2025.
2. Compressed ComputeДля оставшихся chunks: year column (dictionary-encoded, 10 unique years). Filter year=2025: dict_lookup(2025) → index → bitmap. Нет декомпрессии year column. Revenue column: SUM на ALP-encoded данных с bitmap mask.
3. AggregateDuckDB aggregate pipeline: GROUP BY region + SUM(revenue). Region: dictionary-encoded → aggregate per dictionary index (не per string). Revenue: materialized после filter → standard DuckDB aggregate.

Arrow output → DuckDB result

Результат: Arrow-based output в DuckDB. Compressed compute сэкономил: (1) декомпрессию year column, (2) часть декомпрессии revenue column (pruned chunks), (3) string comparison для region (dict indices вместо strcmp).

Использование в DuckDB:

-- Установить и загрузить extension
INSTALL vortex;
LOAD vortex;

-- Чтение Vortex файлов
SELECT * FROM read_vortex('orders.vortex');

-- Запрос с predicate pushdown + compressed compute
SELECT region, SUM(revenue)
FROM read_vortex('s3://bucket/orders.vortex')
WHERE year = 2025
GROUP BY region;

-- Конвертация Parquet → Vortex
COPY (SELECT * FROM read_parquet('orders.parquet'))
TO 'orders.vortex' (FORMAT VORTEX);

Linux Foundation Governance

Vortex передан в LF AI & Data для обеспечения vendor-neutral развития:

Governance: LF AI & Data
BackersMicrosoft (Azure), Snowflake, Palantir, NVIDIA. Четыре крупнейших игрока в data infrastructure. Поддержка = ресурсы на development, production testing, hardware optimization (GPU decode path от NVIDIA).
GovernanceLinux Foundation AI & Data: vendor-neutral governance. Technical Steering Committee. Apache-2.0 license. Open specification. Цель: Vortex как open standard, не proprietary формат одной компании.
EcosystemDuckDB core extension (Jan 2026). SpiralDB: commercial database (Spiral) на Vortex. Iceberg integration: Vortex как data file format в Iceberg tables. Цель: Vortex → Parquet replacement в production query engines.

Итоги

Vortex — принципиально другой подход к колоночному формату:

Ключевые принципы Vortex
ExtensibleКодировки — first-class pluggable объекты. Новая кодировка = новый Rust module, не изменение спецификации. WASM decoders для forward compatibility. FastLanes, ALP, FSST — недоступные в Parquet.
CascadingВложенные кодировки: Dict → BitPack → FastLanes. Каждый уровень добавляет сжатие или ускоряет decode. BtrBlocks-style automatic selection per-chunk.
Compressed ComputeWhite-box encodings: engine видит внутренности encoding'а. Filter на dictionary без развёртывания. SUM на delta без декомпрессии. 5-10x ускорение scan'ов.
EcosystemDuckDB core extension. LF AI & Data governance. Microsoft + Snowflake + Palantir + NVIDIA. Iceberg integration (4x ускорение). Путь: research → production standard.

В следующем уроке мы детально разберём compressed compute — как Vortex выполняет filter, aggregate и join без декомпрессии, включая late materialization, GPU decode path и TPC-H бенчмарки.

Проверьте понимание

Результат: 0 из 0
Прикладной
Вопрос 1 из 4. Vortex файл содержит string-колонку 'city' (50 уникальных городов в 10M строк). Cascading encoding: Dictionary → BitPacked → FastLanes. Какую конкретную цепочку операций выполняет encoder?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 5