Почему обычные базы медленные на аналитике
Корень проблемы — в том, как строки физически лежат на диске.
Строковое хранение (row store) — способ, при котором все поля одной строки лежат на диске подряд. Удобно для OLTP, но губительно для аналитики.
Диск — главное узкое место
Чтобы что-то посчитать, базе нужно прочитать данные с диска в память. Диск читает блоками, и его скорость ограничена. Если запрос вынужден прочитать 100 ГБ ради ответа в три строки, он будет медленным независимо от того, насколько умный движок. Поэтому ключ к быстрой аналитике — читать с диска как можно меньше.
Как строковая база читает лишнее
Возьмём таблицу событий с двадцатью колонками. На диске строки лежат так:
[ строка 1: id, ts, user, url, browser, ... 20 полей ] [ строка 2: id, ts, user, url, browser, ... 20 полей ] [ строка 3: id, ts, user, url, browser, ... 20 полей ] ...
Запрос «средняя длительность сессии» использует одну колонку — duration. Но раз все поля строки лежат вместе, движок не может прочитать только duration: он поднимает с диска целые строки, со всеми двадцатью полями, и отбрасывает девятнадцать из них. Если нужная колонка — 5% объёма, мы зря прочитали 95% данных.
Числа на пальцах
| Параметр | Значение |
| Строк в таблице | 100 000 000 |
| Размер строки | 200 байт |
| Весь объём | ~20 ГБ |
| Нужна 1 колонка | ~1 ГБ полезных данных |
| Прочитает строковая база | все 20 ГБ |
Колоночная база прочитала бы примерно тот самый 1 ГБ — отсюда разница в десятки раз.
А индексы не спасают?
Индексы хороши, когда нужно найти несколько строк по ключу. Но аналитика читает почти все строки таблицы. Идти к каждой через индекс дороже, чем просто прочитать всё подряд, поэтому планировщик честно выбирает full scan — и упирается в тот же диск.
Как работает под капотом
Современные процессоры умеют обрабатывать данные пачками (SIMD, векторизация), когда однотипные значения лежат подряд. В строковом хранилище числа duration разбросаны между текстами URL и именами браузеров — векторизация невозможна, кэш процессора забивается мусором. Колоночное хранение кладёт все duration подряд, и тут включается вся мощь железа.
Частые ошибки
- «Добавлю индексов и станет быстро». Для full-scan аналитики индексы почти не помогают — узкое место в объёме чтения.
- Игнорировать объём колонки. Если из 200-байтной строки нужно 8 байт, строковая база переплачивает чтением в 25 раз.
Итоги
- Диск — узкое место; быстрая аналитика = меньше чтения с диска.
- Строковое хранение заставляет читать все колонки, даже ненужные.
- Индексы помогают точечному поиску, но не массовому сканированию.