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-Oriented Layout
Column-Oriented Layout
Паттерны I/O
Ключевое различие — сколько данных нужно прочитать с диска для типичного запроса.
Аналитический запрос: SELECT AVG(salary) FROM employees
Нужна только одна колонка из 50.
Row: читает ВСЁ
SELECT AVG(salary)
Запрос просит одну колонку, но row layout хранит все колонки вместеЧитает все 50 колонок Нет
Каждый блок содержит все 50 колонок. Нельзя прочитать только salary — данные перемешаныОтбрасывает 49 колонок
98% прочитанных данных выбрасывается — только 1/50 нужна для ответаColumnar: читает 1 колонку
SELECT AVG(salary)
Запрос просит salary — движок знает точное расположение этой колонки из метаданныхЧитает только salary Да
Column pruning — читается только файл/блок с колонкой salary. Остальные 49 не открываютсяИспользуется 100%
Все прочитанные данные используются для вычисления — ноль waste I/OOLTP запрос: SELECT * FROM employees WHERE id = 42
Нужна одна полная строка.
В row-oriented формате: одна операция seek, одно чтение блока — вся строка на месте.
В column-oriented формате: нужно прочитать фрагмент из каждой колонки и собрать строку обратно. 50 колонок — 50 отдельных reads.
Когда что использовать
| Характеристика | Row-Oriented | Column-Oriented |
|---|---|---|
| Идеальный запрос | SELECT * WHERE id = X | SELECT col1, col2 GROUP BY |
| Workload | OLTP (транзакции) | OLAP (аналитика) |
| Латентность записи | Низкая (append строки) | Высокая (разложить по колонкам) |
| Компрессия | Умеренная (разные типы в блоке) | Отличная (однотипные данные) |
| Примеры форматов | CSV, JSON, Avro, MySQL InnoDB | Parquet, ORC, Arrow |
| Примеры движков | PostgreSQL, MySQL, MongoDB | ClickHouse, DuckDB, BigQuery |
Почему columnar сжимается лучше
Однотипные данные в одном блоке — мечта алгоритма компрессии:
Row: разнородные данные
Columnar: однородные данные
Колонка department с 10 уникальными значениями на 1 миллион строк сжимается dictionary encoding в ~100x. В row layout эти значения перемешаны с другими типами — компрессор видит только хаос.
Гибридные подходы
Реальные форматы не бывают чисто row или чисто columnar. Parquet, например — гибридный:
- Файл делится на row groups (группы строк, обычно 128 MB)
- Внутри row group данные хранятся по колонкам
- Это даёт column pruning (читать только нужные колонки) + row group pruning (пропускать целые блоки строк по статистикам)
Этот гибридный подход мы детально разберём в модуле Parquet (02).
Row vs columnar — это не “что лучше”, а “для какого workload”. PostgreSQL (row) идеален для веб-приложений. ClickHouse (columnar) идеален для аналитики. Проблемы начинаются, когда формат не соответствует workload.
Ключевые выводы
- Row layout — все колонки одной строки рядом на диске. Быстрый point lookup, медленная аналитика.
- Columnar layout — все значения одной колонки рядом. Быстрая аналитика, медленный point lookup.
- Column pruning — возможность читать только нужные колонки. Экономит 10–50x I/O на аналитических запросах.
- Компрессия — однотипные данные в колонке сжимаются на порядок лучше, чем разнородные в строке.
- Реальные форматы используют гибридный подход (row groups + columnar внутри).