- PVSM.RU - https://www.pvsm.ru -

Когда вы в последний раз очищали БД от старых записей? А ведь раздувание таблиц и индексов в PostgreSQL из-за неактуальных данных — один из часто недооцениваемых источников «тихих» деградаций. Запросы потихоньку становятся медленнее, бэкапы — тяжелее, а место на диске расходуется неэффективно. В итоге любое лишнее уведомление от алерта или доля секунды задержки могут обернуться сбоем системы.
Привет! На связи Александр Гришин. Я руководитель по развитию продуктов хранения данных Selectel: облачных баз данных [1] и S3-хранилища [2]. В этой статье предлагаю разобраться с одной из тех проблем, которые редко попадают в мониторинг, но легко становятся причиной инцидентов в проде. Посмотрим, чем pg_repack отличается от VACUUM FULL, какие особенности есть у каждого подхода и как использовать repack без дополнительных телодвижений. Статья будет полезна инженерам, поддерживающим PostgreSQL в продакшене, разработчикам облачных приложений и SaaS-сервисов и просто любопытным, кто стремится лучше понять, что происходит под капотом PostgreSQL в разных ситуациях. Погнали!
Используйте навигацию, если не хотите читать текст целиком:
→ Откуда берется bloat [3]
→ Что дает стандартный VACUUM [4]
→ Как работает pg_repack [5]
→ pg_repack в DBaaS Selectel [6]
→ Рассмотрим расширение детальнее [7]
→ Ограничения и грабли [8]
→ Итоги [9]
Bloat (раздувание) — это состояние, когда таблица или индекс занимает на диске значительно больше места, чем реально нужно. Причиной может быть механизм MVCC (многоверсионность), используемый PostgreSQL для обеспечения согласованности транзакций и параллелизма. Вот как это работает.
При выполнении запросов UPDATE или DELETE, старые версии строк помечаются «мертвыми», но физически остаются в файле на диске:
UPDATE — на диске остается старая строка и появляется новая;DELETE — на диске остается старая строка, помеченная как dead.Получается, что чем чаще меняются данные, тем больше пустого места образуется внутри страниц таблицы и индексов. Таблица раздувается, файл на диске растет, падает cache hit ratio, растут I/O, а планировщик отрабатывает менее оптимально. Подробнее эту проблему я уже разбирал в статье об оптимизации PostgreSQL [10]. И как обещал, раскрываю тему дальше — посмотрим, как можно с этим бороться.
Для начала предлагаю вам посмотреть на физический размер вашей таблицы. Например, вот таким образом:
-- Размер таблицы на диске (в байтах)
SELECT pg_relation_size('your_table');
-- В человекочитаемом формате
SELECT pg_size_pretty(pg_relation_size('your_table'));

VACUUM — это команда в PostgreSQL, которая используется для очистки базы данных от «мертвых» (неактуальных) строк и освобождения занимаемого ими места. VACUUM очищает их, чтобы вернуть пространство обратно системе и предотвратить раздувание таблиц. А еще обновляет статистику, важную для EXPLAIN — встроенного оптимизатора запросов.
Есть несколько видов команды.
VACUUM — просто помечает устаревшие строки как доступные для повторного использования (как reusable). Не уменьшает физический размер файлов таблицы.VACUUM FULL — выполняет более глубокую очистку, уплотняет таблицу и возвращает свободное место обратно операционной системе, уменьшая физический размер файла. Этот процесс требует блокировки таблицы, поэтому выполняется дольше и блокирует другие операции.AUTOVACUUM — автоматический процесс в PostgreSQL, который запускается в фоне и периодически выполняет VACUUM для поддержания здоровья базы.
VACUUM FULL решает проблему полностью, но эксклюзивно блокирует таблицу. Для продакшн‑нагрузки это почти всегда неприемлемо.
Допустим, есть таблица на 1 ГБ и вы удаляете 70% строк. Рассмотрим, как это работает.
VACUUM. Таблица все еще весит 1 ГБ на уровне файловой системы в ОС, но ~700 МБ может быть повторно использовано PostgreSQL.VACUUM FULL таблица сжимается, допустим, до 300 МБ, т. к. PostgreSQL копирует только живые строки в новый файл, а затем подменяет им старый, освобождая место на уровне ОС.Простая команда:
VACUUM;
SELECT, INSERT, UPDATE, DELETE.
Можно запустить VACUUM для конкретной таблицы:
VACUUM public.orders;
Добавим обновление статистики для планировщика запросов:
VACUUM ANALYZE public.orders;
Полностью перепишим таблицу в новый файл:
VACUUM FULL products;
Дополнительно для лучшего понимания механики работы можно использовать параметр VERBOSE. Он выводит подробную информацию о процессе вакуума.
VACUUM VERBOSE public.orders;
Пример вывода:

Эта механика станет понятнее, если представить таблицу в PostgreSQL как обычный рабочий блокнот.
DELETE), PostgreSQL просто зачеркивает ее, но не вырывает лист.VACUUM смотрит на зачеркнутые строки и помечает их как «теперь доступные». В будущем PostgreSQL сможет снова записать туда что-то.
Теперь рассмотрим механику работы VACUUM FULL в этой аналогии.
VACUUM), но кажется, что место используется неэффективно.
pg_repack [11] — это расширение для PostgreSQL, которое удаляет мертвые строки, оставшиеся после DELETE и UPDATE. Это позволяет дефрагментировать и компактно переписать таблицу или индекс без блокировки таблицы, в отличие от VACUUM FULL.
Блокировка все еще нужна, но только на пятом шаге и длится миллисекунды.
| Механизм | Освобождает место | Уменьшает размер файла | Требует блокировку |
| VACUUM | Да | Нет | Нет |
| VACUUM FULL | Да | Да | Да |
| pg_repack | Да | Да | Только на финальной фазе переключения таблиц |
Продолжим представлять таблицу PostgreSQL как рабочий блокнот, в котором мы много пишем, зачеркиваем, иногда полностью переписываем всю информацию в новый.
VACUUM FULL). Но сегодня нам нельзя прерывать работу — кто-то все еще читает и пишет в наш блокнот!
pg_repack начинает:
В сервисе баз данных Selectel [12] расширение ставится кликом в панели, после чего функции pg_repack становятся доступны из PostgreSQL. С полным списком поддерживаемых расширений можно ознакомиться в документации [13].
1. Разверните кластер в панели управления [14].

2. Создайте пользователя.

3. Создайте базу данных.

4. Добавьте расширение

5. Подключитесь и используйте готовую облачную базу данных.

Шаг 1. Подготовим тестовую таблицу и искусственно раздуем ее:
-- Создаем таблицу с 1 млн строк, каждая с payload ~100 байт
CREATE TABLE bloated AS
SELECT id, repeat('x', 100) AS payload
FROM generate_series(1, 1000000) AS id;
Для этого обновим 50% строк, чтобы создать «мертвые» версии старых данных:
UPDATE bloated
SET payload = repeat('y', 100)
WHERE id % 2 = 0;
Шаг 2. Измерим размер до репака:
SELECT pg_size_pretty(pg_total_relation_size('bloated')) AS size_before;
-- Результат: ~200 MB
Шаг 3. В управляемой базе данных Selectel DBaaS его нужно запускать с клиентской машины, подключаясь по внешнему адресу::
pg_repack -k -h <host> -p 6432
-U <user>
-d <database>;
После запуска утилита автоматически создаёт копию таблицы, переносит данные без «мусора» и атомарно подменяет оригинальную таблицу. DML-операции при этом ставятся на короткую паузу в конце процесса (на этапе переключения).
Шаг 4. Проверим размер после:
SELECT pg_size_pretty(pg_total_relation_size('bloated')) AS size_after;
-- Новый результат: ~110 MB
| Метод | Размер «до» | Размер «после» | Время выполнения | Доступ к таблице |
| VACUUM | 200 MB | 200 MB | 3 s | доступна |
| VACUUM FULL | 200 MB | 100 MB | 15 s | заблокирована |
| pg_repack | 200 MB | 110 MB* | 8 s | доступна (pause ≤ 200 мс) |
Почему размер новой таблицы в результате работы pg_repack может быть больше? Дело в том, что во время работы pg_repack в оригинальную таблицу могли приходить новые транзакции (INSERT/UPDATE/DELETE), и они тоже переносятся в новую таблицу.
Можно исполтзовать пробный запуск (dry run)
Для оценки того, что будет перепаковано:
pg_repack --dry-run ...
Будет выведен список объектов, которые будут обработаны.
Каждый новый инструмент имеет свои особенности и ограничения, которые полезно принимать во внимание:
Магии не бывает. Фактически утилита просто переписывает данные из одной таблицы в другую. Это, с одной стороны, позволяет вам обслуживать систему без даунтайма. А с другой, займет больше ресурсов.
Стоит иметь в виду, что интенсивная нагрузка на изменения оригинальной таблицы во время репака сведет на нет всю пользу от данной процедуры. Поэтому в некоторых случаях вам все равно не обойтись без VACUUM FULL. Всегда держите в плане эксплуатации регламентированные окна для обслуживания вашей системы.
VACUUM размечает старые строки как переиспользуемые, но не освобождает физически место на диске.VACUUM FULL удаляет bloat, но блокирует таблицу на все время операции.pg_repack убирает bloat почти без простоя и может подойти для обслуживания при невысокой нагрузке на СУБД.repack.repack_table() можно запускать из внешнего планировщика по расписанию.Если вы замечаете, что запросы стали выполняться медленнее, а размер базы данных растет быстрее, чем ожидалось, pg_repack может дать быстрый и безопасный прирост производительности, особенно в среде с высокими SLA и ограниченным доступом к серверу.
Попробуйте использовать pg_repack в составе облачного PostgreSQL [12] от Selectel — установка в один клик, запуск прямо из SQL и никакой возни с настройкой расширений на сервере.
А еще мы в Selectel недавно выпустили ультимативный по производительности сервис — первый в России DBaaS на выделенных серверах [16]. Подробнее об этой услуге я уже рассказывал в другой статье [17].
Обязательно делитесь вашим мнением и опытом в комментариях. В обозримом будущем я продолжу эту тему и расскажу о других полезных расширениях для PostgreSQL.
Автор: GrishinAlex
Источник [18]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/postgresql/424182
Ссылки в тексте:
[1] облачных баз данных: https://selectel.ru/services/cloud/managed-databases/?utm_source=habr.com&utm_medium=referral&utm_campaign=dbaas_article_bloat-postgresql_260625_content
[2] S3-хранилища: https://selectel.ru/services/cloud/storage/?utm_source=habr.com&utm_medium=referral&utm_campaign=storage_article_bloat-postgresql_260625_content
[3] Откуда берется bloat: #1
[4] Что дает стандартный VACUUM: #2
[5] Как работает pg_repack: #3
[6] pg_repack в DBaaS Selectel: #4
[7] Рассмотрим расширение детальнее: #5
[8] Ограничения и грабли: #6
[9] Итоги: #7
[10] в статье об оптимизации PostgreSQL: https://habr.com/ru/companies/selectel/articles/913572/
[11] pg_repack: https://github.com/reorg/pg_repack
[12] баз данных Selectel: https://selectel.ru/services/cloud/managed-databases/postgresql/?utm_source=habr.com&utm_medium=referral&utm_campaign=dbaas_article_bloat-postgresql_260625_content
[13] в документации: https://docs.selectel.ru/managed-databases/postgresql/add-extensions/#extension-descriptions
[14] в панели управления: https://my.selectel.ru/?utm_source=habr.com&utm_medium=referral&utm_campaign=meselectel_article_bloat-postgresql_260625_content
[15] о том, что нужно PostgreSQL.: https://habr.com/ru/companies/selectel/articles/912996/
[16] DBaaS на выделенных серверах: https://selectel.ru/services/cloud/managed-databases/vdedicated/?utm_source=habr.com&utm_medium=referral&utm_campaign=dbaas_article_bloat-postgresql_260625_content
[17] в другой статье: https://habr.com/ru/companies/selectel/articles/880338/
[18] Источник: https://habr.com/ru/companies/selectel/articles/920826/?utm_source=habrahabr&utm_medium=rss&utm_campaign=920826
Нажмите здесь для печати.