
HTAP внутри OLTP: как мы строили векторизованный движок с самого начала
anishukserg 21 минуту назад HTAP внутри OLTP: как мы строили векторизованный движок с самого начала Сложный 12 мин 750 Базы данных * Высоконагруженные системы * Rust * Системное программирование * Все предыдущие статьи...
Anthropic — What company has the best second artificial intelligence model at the end of June?
В сфере искусственного интеллекта произошло заметное событие. anishukserg 21 минуту назад HTAP внутри OLTP: как мы строили векторизованный движок с самого начала Сложный 12 мин 750 Базы данных * Высоконагруженные системы * Rust * Системное программирование * Все предыдущие статьи этого цикла крутились вокруг одной темы: как сделать OLTP предсказуемым. Мы разбирали p99 latency и почему оно уезжает при смешанной нагрузке. Писали про API-контракты между слоями ядра, про WAL-before-data как фундамент корректного recovery, про Buffer Pool с Clock-sweep и BufferRing, который изолирует полные сканы от горячего рабочего набора.
Всё это фундаментальная инженерная работа для предсказуемого OLTP-ядра. Но есть одна вещь, которую мы намеренно не трогали в предыдущих статьях. С самого первого дня архитектура нашей базы закладывалась как HTAP-ready (Hybrid Transactional/Analytical Processing).
Технические детали
Не «добавим аналитику потом», а «в ядре с нуля есть место для векторизованного выполнения». Это решение влияло на выбор типов данных, формат батчей, структуру планировщика. Эта статья про это направление.
Что реализовано на момент публикации: PhysicalType и колоночные батчи; SelectionVector; RowToColumnBridge и BatchToRowBridge; векторизованные VectorSeqScan и VectorIndexScan; SIMD-ускоренный lower-bound на листовых страницах B-Tree (под отдельным feature-флагом сборки на nightly Rust); автовекторизуемые компилятором фильтрационные кернелы для i64/f64 колонок на стабильной toolchain; векторизованный Hash Join и хэш-агрегации. Что ещё не готово: планировщик пока не умеет автоматически выбирать между VectorSeqScan и VectorIndexScan на основе актуальной статистики; колоночное хранилище на диске (данные сейчас row-store, конвертация происходит через RowToColumnBridge на лету); колоночные хэш-кернелы по типам уже есть, но в горячий путь Hash Join подключаются отдельным шагом. Проблема: почему построчное исполнение плохо считаетИсторически OLTP-базы строились вокруг модели Volcano-итератора, или tuple-at-a-time execution.
Каждый оператор плана выполнения передаёт следующему данные по одной строке таблицы. Для коротких транзакционных запросов вроде SELECT * FROM users WHERE id = 1 это работает отлично: нашли одну строку, вернули, закончили. Проблема проявляется, когда нужно посчитать агрегацию по миллиону строк.
Отраслевые последствия
Построчная модель начинает ощутимо проигрывать по нескольким причинам:Косвенный вызов (dispatch) на каждую строку. Каждое next() в классическом Volcano — это обращение через vtable, и процессор плохо предсказывает такие ветвления (branch misprediction) при высоком темпе чередования операторов. Плохая утилизация кэшей L1/L2.
Строки в row-store лежат вперемежку с разными полями, а нам для агрегации часто нужна только одна колонка. Нет возможности применить SIMD. Процессор умеет обрабатывать сразу несколько значений одной SIMD-операцией, но tuple-at-a-time pipeline не даёт ему такой возможности: данные поступают поштучно.
Для транзакционных запросов всё это незаметно: запрос затрагивает 1–10 строк.
Этот прогресс даёт важные сигналы о будущем отрасли, и технологический мир внимательно наблюдает.





