Разбираем идеи Майкла Стоунбрейкера: от Ingres и Postgres до колоночной аналитики и потоковых систем — какие принципы определяют СУБД сегодня.

Майкл Стоунбрейкер — один из тех исследователей и инженеров, чьи идеи не остались «в лаборатории». Он запускал проекты, которые становились реальными продуктами, а затем — ориентиром для всей индустрии. Когда сегодня обсуждают, почему одни базы данных хороши для транзакций, другие — для аналитики, а третьи умеют работать с потоками событий, в этих разговорах регулярно всплывают концепции, которые он продвигал и проверял на практике.
Это не биография и не перечень заслуг. Здесь важнее цепочка идей: какие проблемы в данных и запросах считались ключевыми в разные эпохи, какие архитектурные решения предлагались, что «взлетело» и стало стандартом де‑факто, а что оказалось полезным только в определённых сценариях.
Мы пройдём по нескольким классам технологий, которые заметно повлияли на современную архитектуру данных:
OLTP — «операционные» транзакции: оформить заказ, списать деньги, обновить статус доставки. Важны скорость коротких операций и корректность.
OLAP — аналитика: посчитать выручку по регионам, сравнить периоды, построить отчёт. Запросы обычно тяжелее и читают много данных.
DWH (хранилище данных) — место, куда собирают данные из разных систем, приводят к единому виду и используют для аналитики и отчётности.
Дальше мы будем возвращаться к этим понятиям, чтобы видеть: какая архитектура решает какую задачу — и почему «одна база на всё» работает не всегда.
Реляционная модель обещала простую вещь: данные хранятся в таблицах, а нужный результат можно получить декларативным запросом, не расписывая пошаговый алгоритм. На практике ранним системам пришлось решить несколько «приземлённых» задач, без которых SQL оставался бы красивой идеей: обеспечить целостность данных, выполнять запросы достаточно быстро и предсказуемо, а также поддерживать параллельную работу пользователей.
Во-первых — целостность. Ограничения (например, уникальность ключей), правила ссылочной связанности и транзакционность превращают таблицы из набора строк в систему, которой можно доверять.
Во-вторых — выполнение запросов. Даже простой SELECT на реальных объёмах данных требует индексирования, грамотной работы с диском и планирования соединений (JOIN), иначе «удобный» язык становится слишком дорогим.
Ingres — один из проектов, показавших, что реляционный подход можно довести до промышленного уровня. Его ценность не только в том, что он поддерживал реляционные запросы, а в том, что он сделал их исполнимыми в реальных условиях: с дисковыми ограничениями, конкуренцией запросов и необходимостью обслуживать разные сценарии — от выборок по ключу до сложных соединений.
Ключевой «невидимый» компонент SQL‑СУБД — оптимизатор запросов. Он выбирает план выполнения: какие индексы использовать, в каком порядке соединять таблицы, где делать фильтрацию и агрегации. Чтобы решать это не вслепую, системе нужна статистика: распределения значений, кардинальности, селективность условий.
От современных SQL‑систем мы уже автоматически ждём того, что когда-то было инновацией: стоимостной оптимизатор, сбор статистики, индексы, транзакции и базовые гарантии целостности — без ручного «подгона» под каждый запрос.
Postgres появился как ответ на простую боль, знакомую любой организации: данные меняются быстрее, чем успевают меняться правила хранения. Стоунбрейкер видел, что «классическая» реляционная модель отлично работает для таблиц и SQL‑запросов, но хуже адаптируется к новым типам данных, нестандартным правилам целостности и будущим требованиям, которые ещё нельзя сформулировать.
Ключевая ставка — расширяемость без поломки привычного подхода. Вместо того чтобы выбрасывать SQL и начинать заново, Postgres развивал идею «объектно‑реляционной» системы: остаётся табличная основа и запросы, но платформа позволяет добавлять новые сущности и поведение.
В ранних идеях проекта важное место занимали правила, триггеры и механизм управления изменениями: данные должны не только лежать, но и «жить» по заданным бизнес‑правилам рядом с местом хранения.
Это не про «сложнее и умнее», а про более дешёвую эволюцию:
Вокруг Postgres сформировалась культура расширений: дополнительная функциональность поставляется как подключаемые модули, а не как «монолитная версия базы». Это снижает порог экспериментов: вы пробуете новую возможность, не ломая ядро и не мигрируя на другую СУБД.
Проектируйте схему и платформу так, чтобы изменения были дешёвыми. Закладывайте точки расширения (типы, индексы, правила валидации), изолируйте нестабильные части модели данных и принимайте заранее, что «финальной схемы» не будет — будет управляемая эволюция.
В классических СУБД чтение и запись часто конкурируют за одни и те же строки. Если читатель «держит» блокировку, писателю приходится ждать; если приоритет у писателя — тормозят отчёты и аналитические запросы. На реальной нагрузке это превращается в очереди, скачки задержек и непредсказуемую производительность.
MVCC (многоверсионный контроль конкурентного доступа) решает задачу иначе: вместо постоянного «перетягивания» блокировок система хранит несколько версий данных и даёт каждому запросу согласованную картину.
Представьте, что каждый запрос читает данные как бы «на момент старта» — по снимку (snapshot). Параллельно транзакция записи не переписывает строку «поверх», а создаёт новую версию. Поэтому чтение обычно не блокирует запись, и наоборот: читатель видит старую подтверждённую версию, писатель готовит новую.
Это особенно важно для смешанных сценариев OLTP+OLAP: короткие транзакции продолжают работать, пока в фоне идут отчёты, и система меньше «замирает» из‑за конфликтов.
Цена MVCC — дополнительные версии, а значит расход места и необходимость уборки. Почти всегда есть механизм очистки (vacuum/garbage collection), который удаляет устаревшие версии, когда они больше никому не нужны.
Отдельный риск — долгие транзакции: пока «старый» снимок жив, система вынуждена хранить старые версии, и очистка замедляется.
Сравнивая продукты, смотрите не только на слово «MVCC» в документации, а на практику:
Эти детали напрямую определяют, будет ли система «мягко» переживать пики нагрузки или начнёт тормозить именно тогда, когда это критично.
Идея «универсальной СУБД», которая одинаково хорошо справляется со всеми задачами, звучит удобно: один продукт, одна команда, единые процессы. Но на практике разные типы нагрузки требуют противоположных архитектурных решений. Именно это наблюдение Стоунбрейкер много раз подчёркивал на примерах систем для транзакций и аналитики.
OLTP (операционные транзакции) обычно означает тысячи коротких запросов в секунду: вставки, обновления, точечные чтения по ключу. Здесь важны минимальные задержки, эффективные индексы, быстрая фиксация транзакций, предсказуемое время ответа.
OLAP/аналитика — это редкие, но тяжёлые запросы: сканирование больших объёмов, агрегации, JOIN’ы, расчёты по историческим данным. Тут выигрывают колоночное хранение, компрессия, векторизованное выполнение, продуманная работа с параллелизмом и кэшами.
Попытка «усреднить» систему приводит к тому, что она:
Частая практическая стратегия — разделять контуры: отдельная БД под OLTP и отдельное хранилище/аналитический движок под отчёты (DWH). Это снижает взаимное влияние нагрузок и позволяет оптимизировать каждую систему под свою работу.
Если хочется меньше компонентов, альтернативой бывает единая платформа с разными режимами хранения и выполнения. Но важно заранее проверить, что она выдержит ваши пики и типовые запросы, а не только «среднюю температуру».
Больше систем — это больше точек отказа и работы для команды: синхронизация данных, контроль качества, управление схемами, мониторинг задержек, трассировка пайплайнов.
Практичный критерий: если аналитика начинает мешать операционным процессам (или наоборот), разделение почти всегда окупается — при условии, что вы инвестируете в интеграцию и наблюдаемость с самого начала.
C‑Store — один из ключевых проектов, показавших, почему аналитические запросы требуют другой «начинки», чем классические OLTP‑СУБД. Его идеи разошлись по современным хранилищам данных и аналитическим движкам настолько широко, что сегодня многие практики кажутся очевидными — хотя когда-то это был радикальный поворот.
В аналитике часто считают агрегаты по небольшому набору полей: «выручка по регионам», «средний чек по каналам», «топ товаров». При строковом хранении приходится читать целые строки, даже если нужны 2–3 столбца из 50.
Колоночный формат делает обратное: система читает только нужные столбцы, то есть меньше данных проходит через диск, сеть и CPU‑кэш. Особенно заметно это на широких таблицах и запросах типа «сканирование + фильтр + агрегация».
Колоночные данные обычно лучше сжимаются: в одном столбце значения однотипны и часто повторяются (категории, статусы, коды). Отсюда два эффекта:
Вторая важная идея — векторная обработка: выполнять операции не по одной строке, а пачками значений (батчами). Это лучше укладывается в архитектуру современных CPU и снижает накладные расходы интерпретации.
Практики C‑Store — колоночные форматы, агрессивное сжатие, батч/векторизация, оптимизация под сканирование больших объёмов — стали основой для многих DWH и MPP‑решений. Даже когда продукт «не чисто колонночный», внутри часто живут похожие принципы.
Если хочется глубже разобраться в выборе между транзакциями и аналитикой, см. также /blog/oltp-vs-olap.
Цена аналитической скорости — сложности с частыми точечными обновлениями и множеством мелких вставок. Колоночные структуры хуже подходят для сценариев, где важны мгновенные одиночные записи и строгая транзакционность на высокой конкуренции. Поэтому на практике часто разделяют контуры: OLTP — отдельно, аналитика/DWH — отдельно, а данные переносятся пакетами или потоками.
MPP (Massively Parallel Processing) — подход, в котором запросы выполняются одновременно на многих узлах кластера. Ключевая идея — не «ускорять один сервер», а добавлять узлы и получать прирост производительности за счёт параллелизма.
В архитектуре shared‑nothing у каждого узла свои CPU, память и диск, а данные заранее распределены по узлам (шардинг/партиционирование). Узлы не делят общий диск или общую память, поэтому меньше конкуренции за ресурсы и проще добавлять мощности: увеличили число узлов — увеличили суммарный объём хранения и вычислений.
План запроса в MPP обычно раскладывается на «фрагменты» (stages), которые исполняются на узлах над локальными кусками данных. Если фильтрация и агрегация могут делаться локально, система старается «поджать» данные как можно раньше.
Самая дорогая часть — операции, требующие перестановки данных между узлами: JOIN и GROUP BY часто запускают пересылку по сети (shuffle/exchange). Поэтому выбор ключа распределения и стратегия соединений напрямую влияют на скорость.
Один из частых врагов параллелизма — перекос данных (skew): когда часть ключей «горячая», один узел получает непропорционально много строк и тормозит весь запрос.
Второй класс проблем — сетевые пересылки и координация: широкие JOIN’ы, неправильный порядок операций, лишние перемещения данных, а также накладные расходы планировщика и синхронизаций.
Современные MPP‑хранилища и аналитические платформы развивают те же принципы: распределение данных, параллельное сканирование и агрегации, минимизация shuffle. На практике это превращается в прикладные правила: продумывать ключи распределения, следить за skew, проектировать модели данных так, чтобы «тяжёлые» соединения происходили как можно локальнее.
Традиционные OLTP‑системы долгое время проектировались «от диска»: данные лежат на медленном носителе, а главная задача — экономить операции ввода‑вывода. Отсюда тяжёлые буферы, сложные индексы и осторожные схемы блокировок.
OLTP в памяти переворачивает приоритеты. Если данные и рабочий набор помещаются в RAM, то главным становится не I/O, а задержки на CPU, синхронизация потоков и «цена» конкурентного доступа. Часто упрощается модель хранения (например, меньше уровней абстракции и накладных расходов), а журналирование и репликация выносятся в отдельные, более предсказуемые контуры.
Проекты Стоунбрейкера H‑Store и его коммерческое продолжение VoltDB популяризировали ключевую идею: масштабировать транзакции через партиционирование данных и ограничение формы транзакций.
Подход особенно силён там, где операции частые и однотипные, а запросы предсказуемы: расчёт лимитов и балансов, телеметрия с быстрыми апдейтами, игровые или рекламные счётчики, бронирования с понятным ключом разделения. В таких сценариях «сначала партиционирование» даёт линейное масштабирование и очень низкие задержки.
Слабое место — кросс‑партиционные транзакции: как только одной операции нужно затронуть несколько разделов, появляется дорогая координация и риск падения производительности. Аналогично, сложные отчёты и произвольная аналитика поверх транзакционных данных обычно лучше живут в отдельном контуре (DWH/OLAP), чем внутри memory‑OLTP.
Потоковую обработку полезно выделять в отдельный класс систем, потому что у неё другая «физика» данных: не запросы к уже лежащим таблицам, а непрерывный поток событий, который нужно обработать сразу, пока он не потерял актуальность. Это меняет приоритеты — важнее задержка (latency) и стабильность обработки, чем максимальная скорость разового SQL‑запроса.
Типичные сценарии почти всегда завязаны на события и реакцию:
Идея, которую продвигали исследовательские проекты Стоунбрейкера (вроде Aurora/Borealis), в том, что запрос становится долговременным: вы один раз задаёте логику, а система применяет её ко всем новым данным.
В потоках почти всегда нужен window — окно времени или количества событий, внутри которого считаются агрегаты. Важно различать время события (когда оно произошло) и время обработки (когда дошло до системы). Из‑за задержек появляются поздние данные: событие могло случиться «в прошлой минуте», но приехать позже. Тогда система должна уметь корректировать результаты (например, пересчитать окно) или явно ограничивать, насколько «поздние» события принимаются.
Потоки редко живут сами по себе: чаще они питают хранилища и витрины.
Практичный паттерн: поток → оперативные агрегаты для real‑time дашбордов → выгрузка в DWH для исторической аналитики. При этом полезно заранее определить, какие вычисления делать в потоке (быстро, но ограниченно), а какие — в DWH (глубже, но с задержкой).
Хороший результат даёт единая модель данных и договорённость о «истине»: где формируются итоговые показатели и как они пересчитываются при поздних событиях.
Долгое время «база данных» ассоциировалась с таблицами и строками. Но в реальных продуктах данные часто выходят за рамки классической схемы: координаты и геометрии, тексты, временные ряды, массивы, полу‑структурированные документы. Подход Стоунбрейкера (особенно в Postgres) был в том, чтобы не заставлять всё притворяться таблицами, а расширять модель данных — и при этом сохранять преимущества СУБД: транзакционность, запросы, оптимизацию.
Примеры «нестандартных» типов, которые быстро становятся нормой:
Ключевая идея здесь — приближать вычисления к данным. Если СУБД умеет хранить тип и выполнять над ним операции, то фильтрация и агрегация происходят там же, где лежат данные: меньше пересылок, меньше промежуточных выгрузок, проще контролировать консистентность.
Важно оценивать не «поддерживает ли она тип данных», а насколько полно он встроен в ядро запросов:
Заранее зафиксируйте, какие запросы будут «нормой»: поиск рядом на карте, фильтры по событиям во времени, полнотекст, смешанные условия по JSON‑полям. От этого зависит, стоит ли делать ставку на расширяемость (типы/операторы/индексы) или лучше выделять специализированное хранилище под отдельный класс задач.
Многие идеи, которые Стоунбрейкер продвигал в разных проектах, сегодня воспринимаются как «само собой разумеющееся» — даже если в конкретном продукте они реализованы иначе или называются по‑другому.
MVCC (многоверсионность) стало базовым способом давать параллельный доступ без постоянных жёстких блокировок. Практический эффект понятен бизнесу: меньше «очередей» на записи и чтение, стабильнее время ответа при росте нагрузки.
Расширяемость и плагины тоже превратились в стандарт ожиданий. Пользователи хотят подключать новые типы данных, индексы, функции, интеграции — и делать это без переписывания ядра. Это прямо продолжает линию Postgres: база данных как платформа, а не «закрытая коробка».
Колоночное хранение и векторное выполнение стали нормой для аналитики. Даже там, где система формально «строчная», часто появляются колоночные форматы, отдельные движки или ускорители, потому что стоимость сканирования больших объёмов и время на отчёты критичны.
MPP и параллелизм в аналитических запросах — ещё один де‑факто стандарт: данные и вычисления распределяют по узлам, чтобы укладываться в окна отчётности.
Облака, дешёвое хранение и автоматизация сдвинули акценты: теперь важны не только алгоритмы, но и эксплуатация — автоподстройка, эластичность, предсказуемая стоимость, управляемые сервисы.
Отсюда практический вывод: архитектурные решения важно проверять не только в «теории оптимальности», но и в том, насколько быстро команда может их развернуть, повторить, обновить и откатить.
Идеи Стоунбрейкера полезны не как «история технологий», а как набор критериев: под разные задачи нужны разные компромиссы. Перед тем как выбирать СУБД или строить платформу, полезно пройтись по нескольким вопросам.
Определите, какие запросы будут доминировать:
Эта классификация сразу подсказывает, где уместны MVCC, колоночное хранение, MPP или потоковая обработка — и где это будет лишним.
Два рабочих подхода:
Выбор зависит от того, что дороже: операционная сложность или потери от компромиссного «одного размера для всех».
Архитектура — это не только хранилище. Минимальный набор контуров:
Если вы часто делаете такие контуры «с нуля», полезно иметь быстрый способ собирать вспомогательные сервисы и интерфейсы: загрузчики, админки, простые витрины, дашборды для проверки качества данных. Например, в TakProsto.AI можно через чат собрать прототип веб‑интерфейса (React), серверной части (Go) и схемы в PostgreSQL, а затем развернуть, подключить домен и при необходимости экспортировать исходники. Это удобно именно на этапе, когда вы валидируете гипотезы по данным и проверяете нагрузочный профиль до того, как «зацементируете» архитектуру.
Отдельно для дата‑контуров важны практики управления изменениями. Здесь помогают функции вроде снапшотов и отката (rollback): легче безопасно экспериментировать со схемой, пайплайнами и версиями витрин.
Заранее зафиксируйте метрики: p95 задержки, стоимость запроса, время до инсайта, операционная сложность (сколько людей и регламентов нужно, чтобы это поддерживать).
Частые ошибки: преждевременная распределённость, игнорирование перекоса данных (одни ключи «горячее» других), и отсутствие наблюдаемости (нет понятных метрик, трассировки и причин деградации).
И ещё одна практичная вещь: чем быстрее вы можете собрать тестовый стенд и воспроизвести характерную нагрузку, тем меньше «архитектуры на вере». В этом смысле платформы, которые ускоряют разработку через чат‑подход (vibe‑кодинг) и дают быстрый деплой в российском контуре, помогают проверять решения раньше и дешевле — особенно когда нужно сравнить несколько вариантов (например, один контур vs. разделение OLTP/DWH, или разные ключи партиционирования).
Он важен тем, что продвигал и проверял на практике идеи, которые сегодня считаются «нормой» в архитектуре данных: стоимостной оптимизатор и статистика в SQL‑СУБД, расширяемость платформы (типы/индексы/функции), MVCC для конкурентного доступа, колоночная аналитика и MPP‑параллелизм, а также потоковая обработка событий.
Практический вывод: выбирая СУБД и контуры (OLTP/OLAP/потоки), стоит мыслить не «брендом», а классом нагрузки и компромиссами.
Ingres показал, что декларативные запросы можно сделать исполняемыми в реальных условиях: с ограничениями диска, конкуренцией пользователей, индексами и транзакциями.
Если упростить, Ingres закрепил ожидание, что SQL‑СУБД — это не только язык запросов, но и:
Оптимизатор решает, как выполнять запрос: какие индексы брать, в каком порядке соединять таблицы, где фильтровать и агрегировать. Без статистики он выбирает планы почти вслепую.
Что полезно делать на практике:
Ставка Postgres — эволюция без «переписывания всего»: можно добавлять новые типы данных, индексы и поведение, сохраняя SQL как базовый интерфейс.
Полезные ориентиры:
MVCC даёт чтению «снимок» данных на момент старта запроса, а записи создают новые версии строк. Поэтому чтение обычно не блокирует запись и наоборот.
Что важно проверить в конкретной СУБД:
Потому что требования конфликтуют:
Практичный подход:
См. также: /blog/oltp-vs-olap.
В аналитике часто нужны 2–3 столбца из широкой таблицы. Колоночный формат читает только нужные колонки — меньше I/O, лучше кэш‑локальность.
Дополнительные ускорители:
Ограничение: частые точечные обновления и мелкие вставки обычно даются хуже, чем в OLTP‑движках.
MPP распределяет данные и выполнение по узлам (shared‑nothing). Запрос разбивается на стадии, часть работы делается локально, а самые дорогие места — пересылки данных по сети (shuffle/exchange) для JOIN/GROUP BY.
Что помогает:
Если рабочий набор в RAM, узким местом становится не диск, а CPU и синхронизация. Подход «memory‑OLTP + партиционирование» старается сделать так, чтобы большинство транзакций касались одной партиции.
Практическая эвристика:
Ключевые понятия — окна (window) и два времени:
Из-за задержек появляются «поздние» события, поэтому нужно заранее решить: