Learning Platform
Глоссарий Troubleshooting
Урок 02.02 · 25 мин
Начальный
Row StorageColumnar StorageI/O PatternsOLTPOLAP

Row vs Columnar

Два способа хранить таблицу

Представьте таблицу с 4 строками и 3 колонками: id, name, salary. Есть два фундаментально разных способа записать её на диск.

Row-oriented — записываем строку за строкой:

[1, "Alice", 75000] [2, "Bob", 82000] [3, "Carol", 91000] [4, "Dave", 68000]

Column-oriented — записываем колонку за колонкой:

[1, 2, 3, 4] ["Alice", "Bob", "Carol", "Dave"] [75000, 82000, 91000, 68000]

Кажется, что разница тривиальная. Но при миллиардах строк эта разница определяет, будет запрос выполняться 15 секунд или 15 минут.

Как это выглядит на диске

Row vs Columnar: физический layout

Row-Oriented Layout

Блок 1Первая строка — все колонки вместе. Быстрый доступ к полной записи одного объекта.
Блок 2Вторая строка — опять все колонки. Row layout оптимален, когда нужна вся строка целиком.
Блок 3При SELECT * row layout читает данные последовательно без прыжков по диску.
Блок 4Но при SELECT salary — читается каждый блок целиком, даже если нужна одна колонка.
Все колонки одной строки — рядом на диске

Column-Oriented Layout

Колонка: idВсе значения одной колонки лежат рядом. Однотипные данные — лучшая компрессия.
Колонка: nameПри SELECT salary — эту колонку можно полностью пропустить (column pruning).
Колонка: salaryТолько эта колонка читается при SELECT AVG(salary). Остальные не трогаются.
Все значения одной колонки — рядом на диске

Паттерны I/O

Ключевое различие — сколько данных нужно прочитать с диска для типичного запроса.

Аналитический запрос: SELECT AVG(salary) FROM employees

Нужна только одна колонка из 50.

I/O при аналитическом запросе

Row: читает ВСЁ

SELECT AVG(salary)

Запрос просит одну колонку, но row layout хранит все колонки вместе

Читает все 50 колонок Нет

Каждый блок содержит все 50 колонок. Нельзя прочитать только salary — данные перемешаны

Отбрасывает 49 колонок

98% прочитанных данных выбрасывается — только 1/50 нужна для ответа
I/O: 100% данных → используется 2%

Columnar: читает 1 колонку

SELECT AVG(salary)

Запрос просит salary — движок знает точное расположение этой колонки из метаданных

Читает только salary Да

Column pruning — читается только файл/блок с колонкой salary. Остальные 49 не открываются

Используется 100%

Все прочитанные данные используются для вычисления — ноль waste I/O
I/O: 2% данных → используется 100%

OLTP запрос: SELECT * FROM employees WHERE id = 42

Нужна одна полная строка.

В row-oriented формате: одна операция seek, одно чтение блока — вся строка на месте.

В column-oriented формате: нужно прочитать фрагмент из каждой колонки и собрать строку обратно. 50 колонок — 50 отдельных reads.

Когда что использовать

ХарактеристикаRow-OrientedColumn-Oriented
Идеальный запросSELECT * WHERE id = XSELECT col1, col2 GROUP BY
WorkloadOLTP (транзакции)OLAP (аналитика)
Латентность записиНизкая (append строки)Высокая (разложить по колонкам)
КомпрессияУмеренная (разные типы в блоке)Отличная (однотипные данные)
Примеры форматовCSV, JSON, Avro, MySQL InnoDBParquet, ORC, Arrow
Примеры движковPostgreSQL, MySQL, MongoDBClickHouse, DuckDB, BigQuery

Почему columnar сжимается лучше

Однотипные данные в одном блоке — мечта алгоритма компрессии:

Компрессия: row vs columnar

Row: разнородные данные

Один блокЧисла, строки, даты вперемешку — компрессор не может найти повторяющиеся паттерны
int + string + int + date + bool → сжатие ~2-3x

Columnar: однородные данные

Колонка departmentОдинаковые строки повторяются тысячи раз — dictionary encoding + RLE сжимают в 100x
Повторяющиеся строки → dictionary encoding → сжатие ~50-100x

Колонка department с 10 уникальными значениями на 1 миллион строк сжимается dictionary encoding в ~100x. В row layout эти значения перемешаны с другими типами — компрессор видит только хаос.

Гибридные подходы

Реальные форматы не бывают чисто row или чисто columnar. Parquet, например — гибридный:

  • Файл делится на row groups (группы строк, обычно 128 MB)
  • Внутри row group данные хранятся по колонкам
  • Это даёт column pruning (читать только нужные колонки) + row group pruning (пропускать целые блоки строк по статистикам)

Этот гибридный подход мы детально разберём в модуле Parquet (02).

NOTE

Row vs columnar — это не “что лучше”, а “для какого workload”. PostgreSQL (row) идеален для веб-приложений. ClickHouse (columnar) идеален для аналитики. Проблемы начинаются, когда формат не соответствует workload.

Ключевые выводы

  1. Row layout — все колонки одной строки рядом на диске. Быстрый point lookup, медленная аналитика.
  2. Columnar layout — все значения одной колонки рядом. Быстрая аналитика, медленный point lookup.
  3. Column pruning — возможность читать только нужные колонки. Экономит 10–50x I/O на аналитических запросах.
  4. Компрессия — однотипные данные в колонке сжимаются на порядок лучше, чем разнородные в строке.
  5. Реальные форматы используют гибридный подход (row groups + columnar внутри).

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

Результат: 0 из 0
Концептуальный
Вопрос 1 из 4. Запрос SELECT AVG(salary) FROM employees на таблице с 50 колонками. Сколько данных прочитает row-oriented vs columnar формат?

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

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

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

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