1. Архитектура процессов, управление памятью (Shared Buffers) и принципы MVCC
Архитектура процессов, управление памятью (Shared Buffers) и принципы MVCC
PostgreSQL — это объектно-реляционная СУБД, построенная на классической клиент-серверной архитектуре. Понимание того, как база данных работает «под капотом», отличает простого пользователя SQL от инженера, способного проектировать высоконагруженные системы. Мы разберем три фундаментальных столпа: как устроены процессы, как распределяется память и как работает конкурентный доступ.
Архитектура процессов
В отличие от многих современных систем, использующих многопоточность (threads), PostgreSQL использует многопроцессную модель. Это означает, что каждый компонент системы и каждое подключение клиента — это отдельный процесс операционной системы. Такая изоляция повышает стабильность: падение одного процесса обработки запроса не обрушивает всю базу данных.
!Взаимодействие процессов и общей памяти в PostgreSQL
Главный процесс (Postmaster)
При старте PostgreSQL запускается один главный процесс, исторически называемый postmaster (в списке процессов ОС он часто виден как postgres). Его задачи:
Когда клиентское приложение подключается к базе, postmaster порождает (fork) новый процесс — Backend Process (обслуживающий процесс). С этого момента клиент общается только со своим персональным бэкендом. Именно поэтому параметр max_connections так важен: каждый новый клиент потребляет ресурсы ОС на создание и поддержание отдельного процесса.
Фоновые процессы (Background Processes)
Для обслуживания системы работают служебные процессы, не связанные напрямую с клиентами:
* Background Writer (BGWriter): Его задача — постепенно сбрасывать измененные («грязные») страницы из оперативной памяти на диск. Это делается для того, чтобы процесс Checkpointer не создавал гигантскую нагрузку ввода-вывода в момент контрольной точки.
* Checkpointer: Периодически выполняет контрольную точку (checkpoint) — гарантированный сброс всех грязных данных на диск. Это сокращает время восстановления после сбоя.
* WAL Writer: Сбрасывает журнал предзаписи (Write-Ahead Log) из буфера на диск, гарантируя, что транзакция зафиксирована надежно.
* Autovacuum Launcher: Планировщик, который запускает процессы очистки мусора (о них поговорим в разделе MVCC).
Управление памятью: Shared Buffers
Память в PostgreSQL делится на две большие категории: локальная память (для каждого процесса) и разделяемая память (общая для всех).
Разделяемые буферы (Shared Buffers)
Это ключевой компонент производительности. PostgreSQL не читает и не пишет данные напрямую на диск байт за байтом. Все операции происходят страницами (по умолчанию размер страницы — 8 КБ). Чтобы прочитать строку, база данных должна загрузить всю страницу, содержащую эту строку, в оперативную память.
Область памяти, отведенная под эти страницы, называется shared_buffers. Это кэш базы данных.
> PostgreSQL хранит все данные в страницах, размер которых по умолчанию равен 8 Кб. Однако напрямую читать и писать страницы с/на диск было бы дороговато. Поэтому используется кэш в разделяемой памяти. eax.me
Алгоритм работы:
shared_buffers.Для вытеснения используется алгоритм Clock Sweep (разновидность LRU), который оценивает полезность страницы на основе частоты обращений к ней.
Двойная буферизация
Важно понимать, что PostgreSQL полагается и на кэш операционной системы. Когда Postgres пишет данные на «диск», он фактически передает их в страничный кэш ОС. Операционная система сама решает, когда физически записать биты на магнитный диск или SSD. Это приводит к эффекту двойной буферизации: данные могут храниться и в shared_buffers, и в кэше ОС одновременно. Поэтому не рекомендуется выделять под shared_buffers более 25-40% всей оперативной памяти сервера.
Локальная память (Work Mem)
Каждый backend-процесс также требует личной памяти для выполнения запросов. Параметр work_mem определяет объем памяти, который может быть использован для одной операции сортировки или хеширования (например, для ORDER BY, DISTINCT или Hash Join).
Рассчитаем потенциальное потребление памяти:
Где: * — общий объем оперативной памяти. * — размер общего буфера. * — максимальное число подключений. * — лимит памяти на одну операцию. * — среднее количество сложных операций в одном запросе (сортировок/хеширований).
Пример: Если у вас 100 соединений, и вы выставили work_mem = 100 МБ, то теоретически при сложной нагрузке процессы могут попытаться занять 10 ГБ (100 * 100 МБ), что может привести к ошибке Out Of Memory (OOM), если у сервера всего 8 ГБ RAM.
Принципы MVCC (Multi-Version Concurrency Control)
Классическая проблема баз данных: что делать, если один пользователь читает таблицу, а второй в этот же момент пытается её изменить? Блокировать читателя? Блокировать писателя?
PostgreSQL решает это через MVCC — многоверсионное управление конкурентным доступом. Главное правило MVCC:
> Чтение не блокирует запись, а запись не блокирует чтение.
Как это работает: xmin и xmax
В PostgreSQL при обновлении строки (UPDATE) старая строка не удаляется физически и не перезаписывается. Вместо этого:
Каждая строка (кортеж) имеет скрытые системные поля:
* xmin: ID транзакции, которая создала эту версию строки.
* xmax: ID транзакции, которая удалила (или обновила) эту версию строки.
!Механизм версионирования строк при операции UPDATE
Рассмотрим пример на числах. Текущая транзакция имеет ID = 110.
xmin = 110, xmax = 0 (или NULL). Она видна всем транзакциям, начавшимся после фиксации транзакции 110.xmax прописывается 110. Строка считается «мертвой» для всех будущих транзакций, но «живой» для тех, кто начал работу до транзакции 110.xmax = 110. Новая версия создается с xmin = 110 и xmax = 0.Снимки данных (Snapshots)
Когда транзакция начинает выполнение запроса, она делает «снимок» состояния базы. Она видит только те строки, у которых:
* xmin меньше ID текущей транзакции (они были созданы в прошлом).
* xmax либо пуст, либо больше ID текущей транзакции (они еще не были удалены на момент начала нашего взгляда).
Это обеспечивает изоляцию транзакций. Вы видите базу такой, какой она была на момент начала вашего запроса, даже если параллельно кто-то удалил половину таблицы.
> MVCC в PostgreSQL — это основополагающий механизм, обеспечивающий высокую производительность при параллельной работе с базой данных. В отличие от традиционных систем блокировок, MVCC позволяет транзакциям видеть свои собственные "снимки" данных. it-classic.ru
Проблема Bloat и VACUUM
Платой за MVCC является «мусор». Мертвые версии строк (у которых xmax указывает на завершенную транзакцию) занимают место на диске и в shared_buffers. Если их не чистить, таблица раздувается (bloat), и запросы начинают тормозить, так как им приходится перебирать гигабайты мертвых данных.
Для решения этой проблемы существует процесс VACUUM (и его автоматическая версия Autovacuum). Он сканирует страницы, находит мертвые кортежи и помечает место, которое они занимали, как свободное для повторного использования новыми данными.
Итоги
Postmaster управляет системой, а Background Writer и Checkpointer обеспечивают сохранность данных на диске.shared_buffers — это основной кэш страниц данных. work_mem — это локальная память для операций сортировки, которая выделяется каждому процессу отдельно и требует осторожной настройки.UPDATE создает новую версию, а не перезаписывает старую. Это позволяет читать данные без блокировок, но требует регулярной очистки мусора (VACUUM).