Колоночное хранение
| Суть | значения одного столбца хранятся подряд |
|---|---|
| Противоположность | строковое хранение (row store, OLTP) |
| Сильные стороны | сжатие, чтение нужных столбцов, векторизация |
| Слабые стороны | точечные обновления и вставки по одной строке |
| СУБД | ClickHouse, DuckDB, Vertica, Apache Druid, Snowflake, BigQuery, Redshift |
| Форматы | Apache Parquet, ORC (на диске), Apache Arrow (в памяти) |
Колоночное хранение (англ. columnar storage, column-oriented) — способ физической организации таблицы, при котором значения каждого столбца хранятся на диске и в памяти подряд, отдельно от других столбцов. Классические реляционные СУБД вроде PostgreSQL и MySQL хранят данные по строкам: все поля одной записи лежат рядом. Колоночная организация делает аналитические запросы по огромным таблицам в десятки и сотни раз быстрее и лежит в основе ClickHouse, облачных хранилищ данных и форматов Parquet и Arrow.[1]
Строки или столбцы
Пусть таблица events содержит миллиард строк и 50 столбцов, а запрос считает выручку по дням:
SELECT toDate(ts) AS day, sum(amount)
FROM events
WHERE country = 'RU'
GROUP BY day;
Запросу нужны только три столбца — ts, amount, country. Строковая СУБД вынуждена прочитать с диска все 50 столбцов каждой строки; колоночная читает только три файла, то есть примерно в 15 раз меньше данных ещё до сжатия.
| Строковое (OLTP) | Колоночное (OLAP) | |
|---|---|---|
| Типичный запрос | «заказ № 1042 целиком» | «сумма по миллиардам строк» |
| Чтение одной записи | быстро | медленно (сборка из многих столбцов) |
| Вставка, обновление по одной строке | быстро | дорого, лучше пачками |
| Агрегаты по немногим столбцам | медленно | очень быстро |
| Сжатие | умеренное | сильное, часто в 5–20 раз |
| Примеры | PostgreSQL, MySQL, SQLite | ClickHouse, DuckDB, BigQuery |
Поэтому системы обычно делят по нагрузке: транзакционная база (OLTP) обслуживает приложение, а данные из неё копируются в аналитическое колоночное хранилище (OLAP).
Почему это быстро
1. Сжатие. Значения одного столбца однотипны и часто похожи, поэтому они хорошо сжимаются специализированными кодировками:[3]
- словарная — строки с небольшим числом вариантов (страна, статус) заменяются короткими номерами;
- RLE (run-length) — повторы «RU, RU, RU…» хранятся как «RU × 1000»;
- дельта — для отсортированных времён и счётчиков хранятся разности соседних значений;
- поверх — универсальные алгоритмы LZ4 и Zstandard.
Сжатие уменьшает не только диск, но и объём чтения — а именно ввод-вывод обычно ограничивает аналитику.
2. Векторизованное выполнение. Движок обрабатывает данные не по одной строке, а блоками по тысячам значений одного столбца. Это хорошо ложится на кэши процессора и SIMD-инструкции; подход предложен в исследовательской СУБД MonetDB/X100 (2005).[7]
3. Пропуск данных. Для блоков хранятся минимумы и максимумы, разреженные индексы и фильтры Блума, так что блоки, заведомо не подходящие под условие WHERE, не читаются вовсе.
4. Параллелизм. Столбцы и блоки обрабатываются независимо на всех ядрах и узлах кластера.
История
Идея известна с 1970-х, но массовой стала после исследовательских систем MonetDB и C-Store (Майкл Стоунбрейкер и соавторы, 2005),[2] из которой выросла коммерческая Vertica. В 2010-х появились Google Dremel (BigQuery), Amazon Redshift, Apache Druid, Snowflake и открытый ClickHouse от Яндекса (2016). В 2019 году вышла DuckDB — встраиваемая колоночная СУБД, «SQLite для аналитики».[6]
Форматы файлов
Колоночная организация применяется и вне СУБД:
- Apache Parquet — стандартный колоночный формат для озёр данных: файл делится на группы строк, внутри — колонки со сжатием и статистикой.[4] Его читают Spark, pandas, Polars, DuckDB, ClickHouse, Trino.
- ORC — похожий формат из экосистемы Hadoop/Hive.
- Apache Arrow — колоночный формат в памяти для обмена данными между процессами и языками без копирования и сериализации.[5] На нём построены pandas 2.x (опционально), Polars и многие драйверы.
import duckdb
duckdb.sql("""
SELECT country, count(*) AS n, avg(amount)
FROM 'events/*.parquet'
GROUP BY country ORDER BY n DESC
""").show()
Табличные форматы Apache Iceberg и Delta Lake добавляют поверх Parquet-файлов транзакции, версии и эволюцию схемы.
Ограничения
- Точечные обновления и удаления дороги: ClickHouse выполняет их как фоновые «мутации», другие системы — через пометки об удалении и последующее слияние.
- Вставлять данные нужно пачками (тысячи строк и больше), иначе плодятся мелкие части.
- Запросы «одна строка целиком» и множество соединений по ключам эффективнее в строковых СУБД.
- Транзакционные гарантии обычно слабее, чем в OLTP-базах.
Существуют и гибриды (HTAP): колоночные индексы в SQL Server, расширения для PostgreSQL вроде Citus columnar и pg_duckdb, отдельный колоночный движок MariaDB ColumnStore.
См. также
Источники
- Abadi, Boncz, Harizopoulos et al. — The Design and Implementation of Modern Column-Oriented Database Systems (2013)
- Stonebraker et al. — C-Store: A Column-oriented DBMS (VLDB 2005)
- ClickHouse — Why is ClickHouse so fast?
- Apache Parquet documentation
- Apache Arrow — Columnar Format
- DuckDB documentation
- Boncz, Zukowski, Nes — MonetDB/X100: Hyper-Pipelining Query Execution (CIDR 2005)