Postgres Pro Enterprise 13
Управление транзакциями
13
Авторские права
© Postgres Professional, 2023 год.
Авторы: Алексей Береснев, Илья Баштанов, Павел Толмачев
Использование материалов курса
Некоммерческое использование материалов курса (презентации,
демонстрации) разрешается без ограничений. Коммерческое
использование возможно только с письменного разрешения компании
Postgres Professional. Запрещается внесение изменений в материалы
курса.
Обратная связь
Отзывы, замечания и предложения направляйте по адресу:
Отказ от ответственности
Компания Postgres Professional не несет никакой ответственности за
любые повреждения и убытки, включая потерю дохода, нанесенные
прямым или непрямым, специальным или случайным использованием
материалов курса. Компания Postgres Professional не предоставляет
каких-либо гарантий на материалы курса. Материалы курса
предоставляются на основе принципа «как есть» и компания Postgres
Professional не обязана предоставлять сопровождение, поддержку,
обновления, расширения и изменения.
2
Темы
64-битные номера транзакций
Автономные транзакции
Встроенный пул соединений
3
64-битные транзакции
Формат данных
Заморозка
4
32-битные номера
PostgreSQL, Postgres Pro Standard
32-х разрядный xid (~ 4 млрд. транзакций)
зацикливание идентификаторов
необходимость заморозки
1
3
2
32-битные идентификаторы транзакций допускают примерно четыре
миллиарда уникальных значений. В современных реалиях при большой
нагрузке идентификаторы могут закончиться через несколько дней и
работа СУБД станет невозможна. Поэтому применяют заморозку:
версии строк с достаточно большими xmin помечаются как очень
старые и, следовательно, видимые во всех снимках данных. После
заморозки версий с определенным номером во всех таблицах кластера
номер можно использовать повторно .Yн. transaction wraparound).
Заморозка выполняется как одна из задач процесса очистки и активно
использует подсистему ввода-вывода. При большой нагрузке требуется
постоянно мониторить сервер и принимать оперативные меры, если
заморозка не успевает освобождать номера транзакций.
5
Postgres Pro Enterprise
64-х разрядный счетчик транзакций
не подвержены зацикливанию
64-битные номера
табличная страница
– 4 байта
база xid
поле xmin
+
=
xmin
000000c3 0fe9a1be
00000006
000000c3 0fe9a1c4
Использование 64-битного счетчика позволило бы иметь практически
бесконечное число идентификаторов транзакций, отказаться от их
повторного использования и связанных с этим действий по
администрированию.
Но простое увеличение разрядности идентификаторов привело бы
к увеличению размера заголовков версий строк и, как следствие,
к повышенному расходу дискового пространства и ресурсов ввода-
вывода при обработке данных.
Поэтому в Postgres Pro Enterprise реализован компромиссный вариант
64-х битных идентификаторов: в специальной области в конце
страницы хранится 64-битное базовое значение xid, а в полях xmin и
xmax заголовка версии строки — 32-битное смещение. 64-битный
номер транзакции xmin или xmax вычисляется сложением базового
значения и значения, хранящегося в заголовке версии строки.
Такой же формат применяется для идентификаторов
мультитранзакций: базовое значение хранится в специальной области
страницы, а смещение — в поле xmax заголовка версии строки.
6
Заморозка
Синхронная
если смещения не хватает и необходимо увеличить базовое значение
При очистке
уменьшает pg_xact и pg_multixact
vacuum_freeze_min_age = 50 млн
vacuum_freeze_table_age = 150 млн
autovacuum_freeze_max_age = 10 млрд
vacuum_multixact_freeze_min_age = 5 млн
vacuum_multixact_freeze_table_age = 150 млн
autovacuum_multixact_freeze_max_age = 20 млрд
Поскольку смещения в заголовке версии строки 32-битные, диапазон
значений xid в пределах страницы — от базового до базового плюс
2
32
−1. Если в какую-либо версию строки на странице нужно записать
больший номер, сервер увеличивает базовое значение xid. Если при
этом версии строк с небольшими номерами xid препятствуют такому
увеличению, сервер замораживает эти версии «на лету».
Поскольку 64-битные номера транзакций не используются повторно,
своевременная заморозка не критична для работы многоверсионности:
параметр autovacuum_freeze_max_age имеет значение по умолчанию
10 млрд вместо 200 млн в обычном PostgreSQL, и значение
autovacuum_multixact_freeze_max_age также увеличено. Однако
заморозка все же выполняется как часть процедуры очистки, чтобы
уменьшать размер CLOG (подкаталога pg_xact) и статусов
мультитранзакций (pg_multixact).
Ограничением этого решения является то, что очень долгие транзакции
(возраст которой превышает 2
32
), удерживающие снимок, будут мешать
замораживать версии строк. Поэтому, хотя необходимость в регулярной
заморозке отпадает, долгих транзакций по-прежнему стоит избегать.
8
Автономные транзакции
Устройство автономных транзакций
Видимость данных в автономных транзакциях
Автономные транзакции в SQL
Автономные транзакции в PL/pgSQL и PL/Python
Ограничения автономных транзакций
10
Видимость
Снимки родительской и автономных транзакций
независимы
Read Committed видит изменения
Repeatable Read, Serializable не видят изменений
Возможны взаимоблокировки
Родительская и дочерняя автономная транзакция используют
собственные снимки данных, время жизни которых зависит от уровня
изоляции. Поэтому родительская транзакция с уровнем изоляции Read
Committed будет видеть все изменения дочерних автономных
транзакций, а родительская с уровнем изоляции Repeatable Read или
Serializable не увидит никаких изменений.
Автономная транзакция может вызвать взаимоблокировку, если она
попытается получить блокировку ресурса, уже заблокированного
родительской транзакцией.
11
Ограничения
max_autonomous_transactions = 100
Вложенность до 128 уровней
Не поддерживается блокировка FOR UPDATE
Нельзя обращаться к временным схемам
Нельзя изменить уровень изоляции в PL/pgSQL и PL/Python
Не поддерживаются в расширениях
in_memory
online_analyze
Нельзя использовать слоты логической репликации
Во всех сеансах можно запустить не более
max_autonomous_transactions автономных транзакций одновременно
(по умолчанию 100).
Глубина вложенности автономных транзакций — не более 128.
FOR UPDATE не поддерживается, поэтому, например, нельзя
заблокировать строку в родительской транзакции, а в дочерней
автономной пропустить ее с помощью SKIP LOCKED.
В автономной транзакции нельзя обращаться к объектам временных
схем, в частности, ко временным таблицам.
В блоке PL/pgSQL и методе PL/Python нельзя задать уровень изоляции
автономной транзакции, отличающийся от текущего.
Автономные транзакции не работают с расширениями in_memory и
online_analyze.
В автономной транзакции нельзя использовать слоты логической
репликации.
13
Пул соединений
Проблема и решения
Встроенный пул
Подключение
Ограничения пула
14
Проблема и решения
Один сеанс — один обслуживающий процесс
повышенный расход ресурсов
увеличение конкуренции
прогрев локальных кешей
Пул соединений в сервере приложений или драйвере
J2EE, JDBC, ODBC, node-postgres, Psycopg2
Пул соединений как сторонний продукт
PgBouncer, Odyssey
Встроенный пул Postgres Pro Enterprise сохраняет
контекст сеанса
Простейшая архитектура, при которой для каждого клиентского
соединения запускается отдельный обслуживающий процесс, имеет
ряд недостатков, которые при большом количестве клиентов
становятся критичными. Увеличивается конкуренция при обращении
к ресурсам, растут накладные расходы на запуск процессов
(в частности, на наполнение кешей) и освобождение ресурсов при их
завершении. Кроме того, размер многих структур в общей памяти
сервера пропорционален числу клиентских соединений.
По этим причинам в нагруженные многопользовательские системы
добавляют пул соединений, позволяющий нескольким клиентам
использовать одно соединение с сервером. Как правило, для этого либо
применяют отдельное решение, такое как PgBouncer или Odyssey, либо
задействуют возможности сервера приложений или драйвера
PostgreSQL.
При таком подходе администратору приходится обслуживать
дополнительную компоненту архитектуры, а разработчику — учитывать
особенности работы приложения с пулом соединений.
Встроенный пул соединений Postgres Pro Enterprise позволяет
воспользоваться преимуществами пула, сохранив при этом
большинство возможностей, связанных с контекстом сеанса.
15
Postgres Pro
Встроенный пул
процесс
пул
клиент
процесс
клиент
процесс
клиент процесс
session_pool_size = 0
dedicated_databases =
template0,template1,postgres
dedicated_users = postgres
клиент
процесс
пул
клиент
max_sessions = 1000
Для каждой базы данных создается отдельный пул, количество пулов
не ограничено. Если количество процессов в пуле меньше значения
session_pool_size, для нового сеанса запускается новый процесс,
а если предел достигнут, сеанс привязывается к одному из
существующих процессов.
Процесс, к которому привязано несколько сеансов, поочередно
обслуживает их транзакции, сохраняя при этом контекст каждого
сеанса. Поэтому значение session_pool_size следует задавать
достаточно большим, чтобы клиент не ждал слишком долго, пока
завершится транзакция другого соединения.
Одновременно процесс обслуживает не больше max_sessions сеансов.
Если число соединений с одной базой достигнет max_sessions ×
× session_pool_size, следующая попытка подключения вернет ошибку.
Если клиент соединяется с базой данных, входящей в список
dedicated_databases, или подключается под ролью, входящей
в dedicated_users, для него будет создан выделенный процесс, не
входящий в пулы. По умолчанию это сеансы пользователя postgres и
подключения к базам template0, template1 и postgres. Количество
выделенных процессов не ограничено.
16
Postgres Pro
Подключение
процесс
клиент
приемник
connection_pool_workers = 2
процесс
пул
клиент
клиент postmaster
session_schedule = round-robin
idle_pool_backend_timeout = 0
restart_pooler_on_reload = false
Подключение нового клиента устроено следующим образом.
Процесс postmaster слушает входящие соединения. Подключаясь,
клиент передает стартовый пакет данных, в котором указаны
параметры соединения — имя базы данных, имя роли. Postmaster
передает этот пакет одному из процессов-приемников для анализа.
Количество процессов-приемников определяется значением параметра
connection_pool_workers и должно быть достаточным, чтобы анализ
входящих соединений не стал узким местом.
Получив от приемника информацию о базе данных и роли, postmaster
направляет соединение в подходящий пул. Применяется одна из трех
стратегий, которые можно указать в параметре session_schedule:
round-robin назначает процессы по очереди, random — случайным
образом, load-balancing выбирает процесс с наименьшим числом
сеансов.
Когда все соединения, использующие какой-то процесс, завершаются,
процесс продолжает работать. Автоматическое завершение
неиспользуемого процесса по истечении определенного времени
настраивается параметром idle_pool_backend_timeout.
Для перезагрузки рабочих процессов можно установить параметр
restart_pooler_on_reload = true и перечитать конфигурацию.