PostgreSQL
PostgreSQL: история и архитектура
2010 год. Instagram запускается. Один Postgres-сервер. 100 миллионов пользователей. Все говорили: нужен NoSQL, Postgres не выдержит. Выдержал. MVCC позволял читать снимок прошлого состояния пока другой пишет - без блокировок. В 2024 году каждый второй стартап выбирает Postgres по умолчанию. Майкл Стоунбрейкер начал INGRES в 1973, переписал как Postgres в 1986, получил премию Тьюринга в 2014 - единственную в истории за вклад в базы данных.
- **Instagram** - 100 миллионов пользователей на одном Postgres-сервере, MVCC и JSONB для гибкой схемы
- **GitHub** (поиск по репозиториям), **Shopify** (шардинг Postgres под миллионы магазинов), **Apple** (iCloud частично) - Postgres везде, где нужна надёжность
- **pgvector** - эмбеддинги прямо в Postgres без отдельного векторного хранилища; **PostGIS** - геоданные; **JSONB** - документы. Одна СУБД, бесконечная расширяемость
Почему именно PostgreSQL?
**1986 год. Майкл Стоунбрейкер в Беркли запускает проект POSTGRES** - Post-Ingres, наследник его предыдущей СУБД INGRES. Главная идея: база данных должна расширяться как обычная программа. Добавляй свои типы, операторы, методы индексирования - без правки ядра. Это решение 1986 года сегодня живёт в PostGIS, pgvector, TimescaleDB - тысячах extensions, которые делают Postgres универсальным инструментом.
Хронология PostgreSQL
1973 - Стоунбрейкер создаёт INGRES в Беркли. 1986 - переписывает как POSTGRES с расширяемой архитектурой. 1995 - добавлен SQL, проект переименован в PostgreSQL. 1996 - первый open-source релиз. 2005 - Windows. 2017 - логическая репликация (v10). 2014 - Стоунбрейкер получает премию Тьюринга. 2024 - PostgreSQL 17, №1 в DB-Engines по темпу роста. MySQL потерял позиции после покупки Oracle. Postgres - нет.
**Четыре свойства**, которые делают Postgres выбором по умолчанию для серьёзных проектов:
| Свойство | Что означает | На практике |
|---|---|---|
| ACID | Atomicity, Consistency, Isolation, Durability | Данные не потеряются даже при сбое питания |
| MVCC | Multi-Version Concurrency Control | Читатели не блокируют писателей и наоборот |
| Расширяемость | Можно создавать свои типы, операторы, индексы | PostGIS, TimescaleDB, pgvector - всё это extensions |
| SQL-совместимость | Наиболее полная реализация стандарта SQL | Window functions, CTE, lateral joins - всё из коробки |
**Кто использует Postgres?** Apple (iCloud), Instagram (миллиарды строк фото), Spotify (рекомендации), Reddit, Discord, Notion, Supabase. Даже Microsoft предлагает Azure Database for PostgreSQL. В 2024 году Postgres обогнал MySQL по популярности среди разработчиков по опросу Stack Overflow впервые в истории.
**PostgreSQL vs MySQL:** MySQL быстрее на простых SELECT-ах, но PostgreSQL выигрывает на сложных запросах, больших объёмах и аналитике. Для нового проекта в 2024+ PostgreSQL - безопасный выбор по умолчанию.
Какое ключевое архитектурное решение позволяет PostgreSQL поддерживать PostGIS, pgvector и тысячи других расширений?
Процессная модель: process-per-connection
**PostgreSQL использует модель process-per-connection.** Каждое новое подключение - отдельный процесс операционной системы с собственным адресным пространством. MySQL использует потоки (threads). Node.js - event loop. Postgres - процессы. Это решение Стоунбрейкера 1986 года, и оно до сих пор не изменилось.
**Процесс, а не поток!** Каждый backend process - полноценный процесс ОС с собственным адресным пространством. Это означает бóльшую изоляцию (крэш одного клиента не роняет сервер), но и бóльшее потребление памяти (~5-10 МБ на подключение).
**Вспомогательные процессы** работают постоянно в фоне, обеспечивая здоровье сервера:
| Процесс | Задача | Что будет если остановить |
|---|---|---|
| autovacuum | Очищает «мёртвые» строки после UPDATE/DELETE | Таблицы раздуваются, производительность падает |
| WAL writer | Сбрасывает WAL-буферы на диск | Потеря данных при крэше |
| checkpointer | Периодически записывает dirty pages на диск | Долгое восстановление после сбоя |
| stats collector | Собирает статистику для планировщика запросов | Планировщик выбирает плохие планы |
| background writer | Плавно записывает грязные страницы | Всплески I/O при checkpoint |
**Практический вывод:** из-за модели process-per-connection у PostgreSQL есть предел по числу одновременных подключений (обычно 100-300). Для тысяч клиентов нужен connection pooler - PgBouncer или встроенный в приложение пул.
Почему PostgreSQL использует отдельный процесс (а не поток) для каждого клиента?
Shared Memory: общая память процессов
Если каждый клиент - отдельный процесс с собственной памятью, как они работают с одними данными? Через **shared memory** - область оперативной памяти, доступную всем процессам PostgreSQL одновременно. Один кеш на всех. Одна таблица блокировок. Один WAL-буфер. Всё проходит через неё.
**shared_buffers** - главный компонент. Кеш страниц данных, по 8 КБ каждая. Когда backend нужна строка из таблицы - сначала ищет в shared_buffers. Только если страницы нет в кеше - читает с диска и помещает в shared_buffers для всех. Чем больше shared_buffers, тем меньше дисковых чтений. Это главный рычаг производительности.
**WAL Buffers** - буфер для Write-Ahead Log. Прежде чем изменение попадёт на диск, оно записывается в WAL. Это гарантирует durability: даже при сбое питания PostgreSQL восстановит данные из WAL.
**CLOG (Commit Log)** хранит статус каждой транзакции: in progress, committed, aborted. Когда MVCC проверяет видимость строки, он обращается именно к CLOG, чтобы узнать, зафиксирована ли транзакция, создавшая эту строку.
**shared_buffers = 128 МБ по умолчанию.** Это значение подходит для ноутбука разработчика, но не для production. Рекомендация: 25% от общей RAM сервера. На сервере с 16 ГБ shared_buffers обычно ставится в 4GB.
Backend process нужна строка из таблицы users. В каком порядке он ищет данные?
Postmaster: главный дирижёр
**Postmaster - первый и главный процесс PostgreSQL.** Стартует сервер - стартует postmaster. Три роли: инициализирует shared memory, запускает вспомогательные процессы (autovacuum, WAL writer и другие), слушает порт 5432 и при каждом подключении делает fork() нового backend process.
**pg_ctl reload** перечитывает конфигурацию без остановки сервера. Большинство параметров из postgresql.conf можно изменить без рестарта. Однако shared_buffers, max_connections и некоторые другие требуют полного перезапуска.
**Postmaster - единая точка отказа.** Если postmaster падает, все клиенты отключаются. Поэтому код postmaster-а максимально простой и стабильный - вся сложная логика вынесена в backend и вспомогательные процессы.
**Файл postmaster.pid** в директории данных содержит PID, порт и путь к данным. Если PostgreSQL завершился аварийно и этот файл остался - нужно удалить его вручную перед повторным запуском.
Параметр shared_buffers изменён в postgresql.conf. Что нужно сделать, чтобы изменение вступило в силу?
Подключение клиента: от TCP до запроса
При подключении приложения к PostgreSQL разворачивается многоступенчатый протокол. Клиентская библиотека (libpq, psycopg, node-postgres) устанавливает TCP-соединение с postmaster. Аутентификация. Fork нового backend process. И только потом - первый SQL-запрос. Это занимает 50-100 мс. Именно поэтому connection pooling обязателен.
**Каждое подключение = отдельный процесс ОС.** При 500 одновременных подключениях - 500 процессов, каждый потребляет ~5-10 МБ RAM. Для приложений с большим числом клиентов (микросервисы, serverless) это может привести к исчерпанию памяти и CPU.
**Connection pooling решает проблему.** PgBouncer или pgcat держат 20-50 реальных подключений к PostgreSQL и мультиплексируют тысячи клиентских запросов через них. Вместо 500 backend процессов по 5-10 МБ каждый - 20 процессов и пулер. Экономия памяти в 25 раз.
**В Node.js/NestJS** пул подключений уже встроен в драйвер (pg, TypeORM). Параметр `max` в настройках пула - это число реальных подключений к PostgreSQL. Не рекомендуется ставить больше 20-30 на одно приложение.
Каждый SQL-запрос открывает новое подключение к PostgreSQL
Подключение устанавливается один раз и используется повторно. В production обязателен connection pooling - PgBouncer или встроенный пул в драйвере
Установка подключения - дорогая операция: TCP handshake, аутентификация, fork() нового процесса. Это 50-100 мс на каждый запрос. Connection pool держит подключения открытыми, переиспользует их - latency падает до <1 мс
Есть 10 микросервисов, каждый с пулом max=50 подключений. Сколько backend процессов создаст PostgreSQL при полной нагрузке?
Ключевые идеи
- **PostgreSQL - расширяемая ACID-совместимая СУБД** с 35+ летней историей. Стоунбрейкер 1986 → Instagram 100M users → премия Тьюринга 2014
- **Модель process-per-connection**: каждый клиент получает отдельный процесс ОС. Надёжно - крэш одного не роняет сервер. Но требует PgBouncer при масштабировании
- **Shared memory (shared_buffers)** - центральный кеш для всех backend процессов. 128 МБ по умолчанию - в production поднимать до 25% RAM
- **Postmaster** - дирижёр: инициализирует shared memory, слушает порт, форкает backend-ы при каждом подключении
- MVCC - ключевой механизм: читатель и писатель не блокируют друг друга. Именно поэтому Instagram выдержал 100M пользователей на одном сервере
Связанные темы
Архитектура PostgreSQL - фундамент для понимания всех последующих тем:
- Установка и конфигурация — Настройка shared_buffers, max_connections и других параметров, которые мы упоминали
- MVCC и транзакции — MVCC работает через shared memory и CLOG - механизмы, рассмотренные в этом уроке
- WAL и надёжность — WAL writer и WAL buffers - часть архитектуры, которую мы увидели в shared memory
Вопросы для размышления
- Instagram в 2010 году выдержал 100M пользователей на одном Postgres-сервере. Какие конкретные механизмы из этого урока это обеспечили? Что бы случилось без MVCC?
- Приложение создаёт 200 подключений к PostgreSQL. Сколько оперативной памяти уходит только на backend процессы? Когда это становится проблемой?
- Стоунбрейкер в 1986 году выбрал process-per-connection вместо потоков. Это было правильное решение для 2024 года? Что потеряли, что приобрели?
Связанные уроки
- pg-02-install-config — Installation and configuration builds directly on the architecture described here
- alg-01-big-o — Query planning is Big-O analysis applied to database operations
- ds-01-arrays — PostgreSQL heap storage is essentially an array of 8KB pages
- st-01-feedback-loops — PostgreSQL's process model implements self-healing feedback loops via postmaster supervision
- sd-01-intro — System design decisions about SQL vs NoSQL require understanding what PostgreSQL offers
- db-01-intro