Почему обычные базы медленные на аналитике

Корень проблемы — в том, как строки физически лежат на диске.

Строковое хранение (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 раз.

Итоги

  • Диск — узкое место; быстрая аналитика = меньше чтения с диска.
  • Строковое хранение заставляет читать все колонки, даже ненужные.
  • Индексы помогают точечному поиску, но не массовому сканированию.
Проверьте себя
1. Почему строковая база читает лишние данные при аналитическом запросе?
AВсе поля строки лежат на диске вместе, поэтому нельзя прочитать одну колонку отдельно
BДиск умеет читать только целые таблицы
CSQL запрещает выбирать отдельные колонки
DИндексы всегда читают всю таблицу
2. Что является главным узким местом при аналитических запросах?
AСкорость сети
BОбъём данных, читаемых с диска
CКоличество таблиц в базе
DДлина имён колонок