Learning Platform
Глоссарий Troubleshooting
Урок 06.03 · 22 мин
Средний
vectorized-enginevectordatachunk

Vector и DataChunk: колоночный батч

В двух предыдущих уроках мы говорили о «векторах» как о порциях данных, движущихся по конвейеру, и установили, что порция — это около 2048 значений. Пора назвать структуры данных точно. У векторизованного движка DuckDB есть две фундаментальные структуры: Vector и DataChunk. Всё, что течёт между операторами, что обрабатывает каждый оператор, что возвращает запрос, — это DataChunk, составленный из Vector-ов.

Понять их устройство необходимо для всего остального в модуле. Физические типы векторов, selection vector, validity mask, push-based исполнение — всё это надстройки над Vector и DataChunk. Этот урок определяет обе структуры точно и показывает, как они соотносятся с колоночной природой DuckDB.


Vector: массив значений одной колонки

Vector — это массив значений одного типа, относящихся к одной колонке. Если у вас есть колонка amount типа INTEGER, то её вектор — это массив целых чисел: примерно 2048 подряд идущих значений amount.

Ключевые свойства Vector. Первое — однотипность: все значения вектора одного типа данных. Вектор amount — только целые, вектор name — только строки. Один вектор не смешивает типы. Второе — принадлежность одной колонке: вектор представляет фрагмент одной колонки, не нескольких. Третье — фиксированный максимальный размер: вектор содержит не более STANDARD_VECTOR_SIZE (2048) значений, обычно ровно столько, и меньше — только в последней, неполной порции.

Vector — это колоночная структура. В нём значения одной колонки лежат подряд, плотным массивом. Это прямое отражение того, что DuckDB — колоночная СУБД: и на диске колонка хранится по-колоночно, и в памяти при обработке она представлена вектором — массивом значений этой колонки.

Vector — плотный массив одной колонки
Vector колонки amount (INTEGER)Массив значений одного типа, относящихся к одной колонке. Здесь — целочисленные значения колонки amount.
до 2048 значений одного типа
Vector колонки region (VARCHAR)Отдельный вектор — другая колонка, другой тип. Каждая колонка батча представлена своим вектором.

Важно: Vector — это интерфейс, а не один-единственный способ хранения. Снаружи вектор всегда выглядит как «массив из N значений колонки», но физически внутри он может быть устроен по-разному: плоским массивом, одним повторяющимся значением, ссылкой на словарь. Эти физические формы — тема следующего урока. Пока достаточно понимать Vector как логическую единицу: фрагмент одной колонки, до 2048 значений одного типа.


DataChunk: набор векторов одной кардинальности

Отдельный Vector представляет одну колонку. Но запрос работает со строками, у которых много колонок. Структура, объединяющая колонки, — DataChunk.

DataChunk — это коллекция Vector-ов, по одному на каждую колонку, плюс одно общее число — кардинальность (cardinality), количество логических строк в этом чанке.

Главное свойство DataChunk — все его векторы имеют одну и ту же кардинальность. Если DataChunk содержит 1500 строк, то и вектор region, и вектор amount, и вектор qty — все содержат ровно 1500 значений. Это и делает DataChunk представлением набора строк: i-е значение каждого вектора в совокупности образует i-ю строку.

DataChunk — векторы колонок плюс общая кардинальность
DataChunk, cardinality = 1500Коллекция векторов: по одному на колонку. Кардинальность — число логических строк, одно на весь чанк.
состоит из векторов
Vector region (1500)Вектор первой колонки. Содержит ровно cardinality значений.
Vector amount (1500)Вектор второй колонки. Та же кардинальность — 1500 значений.
Vector qty (1500)Вектор третьей колонки. Снова ровно 1500 значений. i-е значения всех векторов образуют i-ю строку.

DataChunk — это и есть «порция», которую мы в прошлых уроках называли единицей движения по конвейеру. Точнее так: между операторами движутся DataChunk-и. Оператор получает на вход DataChunk (набор векторов до 2048 строк), обрабатывает каждый вектор плотным циклом, формирует выходной DataChunk, передаёт дальше. DataChunk — это «колоночный батч»: маленькая горизонтальная нарезка таблицы (до 2048 строк), представленная по-колоночно.

СтруктураЧто представляетРазмерАналогия
VectorФрагмент одной колонкиДо 2048 значений одного типаСтолбец маленькой таблички
DataChunkФрагмент таблицы, набор строкНабор векторов, все одной кардинальностиМаленькая табличка целиком

Почему колоночный батч, а не строчный

DataChunk хранит данные по-колоночно (набор векторов-колонок), а не по-строчно (массив записей). Это сознательный выбор, и он критичен для скорости.

Если бы батч хранился по-строчно — массивом записей, где каждая запись это {region, amount, qty} единым куском, — оператор «прибавить 1 к amount» был бы вынужден прыгать по памяти: значения amount лежали бы не подряд, а через region и qty. Прыжки по памяти убивают кэш процессора и делают невозможным SIMD.

Колоночный DataChunk хранит все amount подряд в одном векторе. Оператор «прибавить 1 к amount» проходит вектор amount плотным линейным циклом — идеально для кэша, идеально для SIMD. Колонки, которые оператору не нужны (region, qty), он просто не трогает — их векторы лежат отдельно.

NOTE

Колоночность DataChunk — это продолжение колоночности всего DuckDB. На диске таблица хранится по колонкам. При чтении колонка превращается в вектор. DataChunk собирает векторы нужных колонок. Оператор обрабатывает каждый вектор плотным циклом. Колоночное представление сквозное — от storage-формата через буферы до структур исполнения, нигде не происходит дорогого «переворота» в строчный вид.

DataChunk — это также то, что DuckDB возвращает как результат запроса. Когда вы выполняете SELECT через Python API и получаете данные, под капотом результат сформирован как последовательность DataChunk-ов. Интеграция с Arrow, Pandas, Polars во многом эффективна именно потому, что колоночный DataChunk напрямую отображается на колоночные форматы этих библиотек без построчного переписывания (это разбирается в модуле про Python-экосистему).


Чанки в потоке: запрос — это последовательность DataChunk-ов

Таблица обычно гораздо больше 2048 строк. Поэтому запрос обрабатывает не один DataChunk, а поток чанков. Сканирование таблицы выдаёт DataChunk за DataChunk-ом: первые 2048 строк, следующие 2048, и так далее. Каждый чанк прогоняется через конвейер операторов, затем берётся следующий.

Внутри движка существует и понятие более крупной группировки чанков. DuckDB оперирует не только отдельными DataChunk-ами, но и наборами чанков — например, при буферизации данных между стадиями параллельного исполнения. В коде движка для размера таких наборов есть внутренняя константа PARTIAL_CHUNK_COUNT — порог порядка примерно 50 векторов. Важно правильно к ней относиться: это внутренняя деталь реализации, а не публичный API и не гарантированное число. Не стоит думать о ней как о жёстком «50 умножить на 2048» — это ориентировочный внутренний порог, который касается организации параллельного исполнения и буферизации; конкретное значение и сама роль константы — внутреннее дело движка и могут меняться между версиями.

WARNING

STANDARD_VECTOR_SIZE (2048) — фундаментальная, стабильная характеристика движка: на ней держится размер вектора, и от неё считается, например, размер row group. А PARTIAL_CHUNK_COUNT — внутренняя константа реализации, описывающая порог порядка примерно 50 векторов для буферизации наборов чанков. Не путайте их статус: первое — то, что нужно знать и на что можно опираться; второе — деталь внутренней механики, упоминаемая для полноты картины, но не число, на которое стоит закладываться в рассуждениях.

Итог урока: Vector — массив до 2048 значений одной колонки одного типа; DataChunk — набор таких векторов, по одному на колонку, все с одной кардинальностью, то есть колоночный батч строк. Между операторами движутся DataChunk-и, запрос целиком — это поток DataChunk-ов от сканирования к результату. Колоночность этих структур сквозная и обеспечивает плотные циклы, дружелюбные к кэшу и SIMD. Всё дальнейшее в модуле — физические типы векторов, selection vector, validity mask, push-based модель — это детали устройства и движения именно Vector и DataChunk.


Попробуй сам

Задания на наблюдение и размышление.

  1. Выполните SELECT current_setting('threads') и любой запрос с EXPLAIN ANALYZE. В выводе плана обратите внимание на счётчики обработанных строк у операторов — мысленно прикиньте, на сколько DataChunk-ов по 2048 строк делится это число.
  2. Объясните своими словами разницу между Vector и DataChunk. Что из них представляет колонку, а что — набор строк?
  3. Почему все векторы внутри одного DataChunk обязаны иметь одинаковую кардинальность? Что сломалось бы, если бы вектор region содержал 1500 значений, а вектор amount — 1490?
  4. Объясните, почему оператор «прибавить 1 к колонке amount» работает быстрее с колоночным DataChunk, чем работал бы со строчным массивом записей.
  5. Почему PARTIAL_CHUNK_COUNT не стоит воспринимать как гарантированное «50 x 2048»? Чем его статус отличается от статуса STANDARD_VECTOR_SIZE?
Arrow Memory Layout: RecordBatch как аналог DataChunk
Проверка знанийKnowledge check
Что такое Vector и DataChunk в движке DuckDB, как они соотносятся, и почему DataChunk хранит данные по-колоночно?
ОтветAnswer
Vector — это массив значений одного типа, относящихся к одной колонке: например, вектор колонки amount это массив целых чисел, примерно 2048 подряд идущих значений amount. Свойства вектора: однотипность (все значения одного типа), принадлежность одной колонке, размер не более STANDARD_VECTOR_SIZE (2048) значений. Vector — это колоночная структура и логический интерфейс 'массив из N значений колонки'; физически внутри он может быть устроен по-разному (плоско, константой, словарём — тема отдельного урока). DataChunk — это коллекция Vector-ов, по одному на каждую колонку, плюс общее число cardinality (кардинальность) — количество логических строк. Главное свойство: все векторы внутри одного DataChunk имеют одну и ту же кардинальность; если чанк содержит 1500 строк, то каждый его вектор содержит ровно 1500 значений, и i-е значения всех векторов в совокупности образуют i-ю строку — поэтому DataChunk и есть представление набора строк, колоночный батч (горизонтальная нарезка таблицы до 2048 строк, представленная по-колоночно). Соотношение: Vector представляет одну колонку, DataChunk — набор строк со всеми колонками; между операторами движутся именно DataChunk-и, а запрос целиком — это поток DataChunk-ов от сканирования к результату. DataChunk хранит данные по-колоночно (набор векторов-колонок), а не по-строчно (массив записей), потому что при строчном хранении значения одной колонки лежали бы вперемежку с другими, и оператор был бы вынужден прыгать по памяти, убивая кэш и делая невозможным SIMD. Колоночный DataChunk держит все значения колонки подряд в одном векторе — оператор проходит его плотным линейным циклом, дружелюбным к кэшу и SIMD, и не трогает ненужные колонки. Колоночность сквозная: от storage-формата через буферы до структур исполнения.

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

Результат: 0 из 0
Концептуальный
Вопрос 1 из 4. Что такое Vector в движке DuckDB?

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

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

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

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