Эти два параметра образуют третью согласованную пару в комплекте автовакуума, после пары для ANALYZE и пары для обычного вакуума (последняя будет рассмотрена следующей). Они управляют тем, когда автовакуум запускает VACUUM для таблицы на основе количества вставок (INSERT) с момента последнего вакуума — не обновлений, не удалений, только вставок. Они были добавлены в PostgreSQL 13 и существуют для решения конкретной проблемы.
Первичный сервер PostgreSQL теряет питание в 2 часа ночи. Запись возобновляется менее чем за тридцать секунд. Дежурный инженер читает оповещение утром, видит, что кластер восстановился самостоятельно, и возвращается к кофе. Это тот результат, который должна обеспечивать высокая доступность PostgreSQL.
Работающий кластер Patroni сам по себе доводит вас лишь до половины пути. Выборы лидера работают. Резервный сервер (standby) повышается до первичного. Состояние кластера в etcd остаётся согласованным. Затем приложение продолжает пытаться подключиться к IP-адресу, который теперь указывает на неправильный узел, старый первичный сервер требует ручного повторного присоединения (rejoin), и дежурный инженер оказывается на конференц-связи, а не в постели.
Я видел этот шаблон достаточно часто, чтобы назвать его стандартным. Кластер делает свою работу. Приложение ждёт человека. Достаётся инструкция (runbook). Время восстановления (RTO) превышает соглашение об уровне обслуживания (SLA). Все соглашаются после этого, что «нам следует серьёзнее отнестись к высокой доступности».
Эти три параметра вместе задают темп работы автовакуума: как часто он запускается, как интенсивно работает во время выполнения и как долго приостанавливается, чтобы не монополизировать ваш ввод-вывод. Как и пара параметров autovacuum_analyze_*, их лучше всего понимать как единое целое.
В предыдущей статье мы видели, как каждое обновление (UPDATE) оставляет после себя мёртвый кортеж. То же поведение «копирования при записи» (copy-on-write) проявляется с операционной точки зрения в статье «DELETEs are difficult». Это плата за MVCC, и если мы имеем дело только с кучей (heap) это терпимо. Проблема в индексах.
Каждое обновление в PostgreSQL потенциально записывает данные во все индексы таблицы, даже если индексированные столбцы не изменились. Пять индексов, один обновлённый столбец? Пять лишних операций записи в индексы, пять новых записей для вакуума, в пять раз больше трафика WAL. При тысячах обновлений в секунду это становится доминирующей стоимостью работы с таблицей, в которой много обновлений.
Heap-Only Tuple (HOT) updates — это механизм PostgreSQL, позволяющий обойти эту проблему. На мой взгляд, это самое изящное оптимизационное решение в движке хранения. Давайте проследим, как именно оно работает.
Этот параметр является multixact-эквивалентом autovacuum_freeze_max_age. Механизм параллелен; защищаемый объект — не пространство идентификаторов транзакций (XID), а пространство идентификаторов MultiXact, о котором большинство пользователей PostgreSQL никогда не задумывались, а остальные узнали о нём во время аварии. Итак, перед рассмотрением параметра — краткий обзор multixact.
За годы обучения, консультирования и поддержки пользователей PostgreSQL я разработал несколько запросов для мониторинга автовакуума. Мониторинг автовакуума — не новое требование, поэтому существует уже много готовых запросов для мониторинга. Однако не все эти запросы полезны. Поэтому я подумал, что будет хорошей идеей написать статью о моей собственной коллекции — как для себя в качестве справочника, так и в качестве услуги для администраторов PostgreSQL по всему миру.
autovacuum_max_workers задаёт максимальное количество фоновых процессов автовакуума (autovacuum worker processes), которые могут выполняться одновременно. Значение по умолчанию — 3. Контекст параметра — postmaster, поэтому его изменение требует перезапуска сервера. Процесс-планировщик (launcher process) является отдельным и не учитывается в этом числе.
Это тот параметр, который кто-то увеличивает с 3 до 10, решив, что автовакуум работает слишком медленно, после чего обнаруживает, что вакуум, как ни странно, на самом деле не стал быстрее. У этого есть причина, и это самое важное, что нужно понять об этом GUC.
Архиватор запускается только после завершения сегмента WAL. В загруженной базе данных это происходит постоянно, а в малоактивной — может не происходить часами или даже днями. Параметр archive_timeout предназначен для того, чтобы избежать ситуации вроде «наша база данных весь день принимала операции записи, но ни одна из них ещё не попала в архив».
archive_mode — это главный выключатель архивации WAL. После трёх предыдущих статей — archive_cleanup_command, archive_command, archive_library — мы наконец добрались до параметра, который решает, будет ли вообще работать вся эта механика.
Прежде чем погрузиться в этот параметр, небольшое исправление (errata) к предыдущей статье. Я сказал, что инструменты резервного копирования «могут зарегистрироваться как archive_library и полностью обойти archive_command» начиная с PostgreSQL 15+. Именно для этого и была предназначена эта функциональность. Однако это не то, что на самом деле появилось в экосистеме. Подробнее об этом чуть позже.
archive_library, добавленный в PostgreSQL 15, позволяет настроить загружаемый C-модуль для обработки архивации WAL вместо команды оболочки (shell command). Процесс архиватора (archiver process) вызывает колбэки модуля напрямую, внутри своего процесса, один раз на каждый сегмент. Никакого fork(). Никакой оболочки. Никакого cp. PostgreSQL передаёт завершённые WAL-файлы модулю и не будет переиспользовать их, пока модуль не подтвердит успех, — контракт тот же, что и у archive_command, но без накладных расходов и режимов отказа, присущих сценариям оболочки.
Алфавитный порядок выдал нам первую «жертву». archive_cleanup_command — это параметр резервного сервера (standby-server knob), который существует исключительно для того, чтобы прибираться после archive_command. Однако алфавит настаивает на том, чтобы отложить рассмотрение archive_command до следующей статьи. Поэтому мы опишем, как прибирать вечеринку, которую ещё не устраивали.
Кратчайшая предыстория: Первичный сервер PostgreSQL (primary) может архивировать свои сегменты WAL в некоторое место — каталог, корзину S3, общий ресурс NFS — выполняя команду оболочки для каждого заполненного сегмента. Резервные серверы (standbys) читают из этого места, чтобы догонять изменения, а инструменты резервного копирования читают из него для обеспечения восстановления на момент времени (point-in-time recovery, PITR). Файлы накапливаются. Кто-то должен их удалять.
Для хорошего бэкенд-разработчика существует определенное стремление к оптимизации запросов (скорости и памяти). Одним из полезных инструментов оптимизации баз данных являются подзапросы SQL.
Подзапрос - это запрос внутри другого запроса. Он позволяет вам вытащить связанные данные или результаты вычислений без написания нескольких запросов или тяжелых соединений. Он также позволяет динамически выполнять вычисления и агрегировать данные.
Большинство GUC в этой серии будут операционно не важны для большинства читателей. Этот — не такой. application_name — это самое дешёвое средство наблюдаемости (observability infrastructure), которое поставляет PostgreSQL, и поразительное количество производственных баз данных работают с неустановленным значением или со значением, застрявшим на значении по умолчанию клиентской библиотеки (psql, PostgreSQL JDBC Driver или, что я люблю больше всего, — пустая строка).
Это метка уровня сеанса (per-session label). Значение по умолчанию — пустая строка, контекст — user, поэтому любая роль может его установить. Установите его через SET application_name = 'order-service';, через параметр подключения application_name или через переменную окружения PGAPPNAME, которую libpq учитывает автоматически. Максимальная длина — NAMEDATALEN - 1 — 63 байта в стандартной сборке, а непечатаемые символы заменяются на ?.
Вот GUC, который поставляется с предупреждающей этикеткой. Документация, обычно сдержанная до степени пародии, прямо заявляет, что неправильная установка этого параметра может привести к «необратимой потере данных или серьёзному повреждению системы базы данных». Когда документация PostgreSQL так повышает голос — прислушайтесь.