← Назад к вопросам
Как работает VACUUM в PostgreSQL?
2.7 Senior🔥 131 комментариев
#DevOps и инфраструктура#Базы данных (SQL)
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
VACUUM в PostgreSQL
VACUUM — это критичный процесс очистки в PostgreSQL, который удаляет мёртвые строки (dead tuples) и восстанавливает дисковое пространство.
Почему VACUUM нужна?
PostgreSQL использует MVCC (Multi-Version Concurrency Control). Когда строка обновляется или удаляется, старая версия не стирается сразу.
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name VARCHAR,
email VARCHAR
);
INSERT INTO users (name, email) VALUES ("Alice", "alice@example.com");
-- БД содержит: 1 живая строка
UPDATE users SET email = "alice.new@example.com" WHERE id = 1;
-- БД содержит:
-- 1 живая строка (новая версия)
-- 1 мёртвая строка (старая версия)
DELETE FROM users WHERE id = 1;
-- БД содержит:
-- 1 мёртвая строка (помечена как удалённая)
-- Дисковое пространство НЕ освобождено!
Как работает VACUUM
1. Маркировка мёртвых строк (Mark phase)
-- VACUUM определяет, какие версии строк больше не нужны
-- Строка мертва, если никакая активная транзакция не нуждается в старой версии
-- Пример
VACUUM users;
-- 1. Начинает сканировать таблицу
-- 2. Смотрит на xmin/xmax (transaction IDs)
-- 3. Если xmax < min_active_transaction, то строка мертва
-- 4. Помечает как removable
2. Удаление мёртвых строк (Removal phase)
-- Физически удаляет помеченные строки со скоростью блока
VACUUM (VERBOSE) users;
-- Вывод:
-- VACUUM: pages: 0 removed, 10 remain, 5 skipped due to pins (approximately 48 live rows, 2 dead rows)
3. Обновление индексов
-- VACUUM также очищает индексы
-- Если в индексе есть указатели на мёртвые строки — они удаляются
CREATE INDEX idx_users_email ON users(email);
VACUUM users;
-- Индекс автоматически очищается
4. Обновление видимости (Visibility map)
-- PostgreSQL ведёт visibility map для быстрого поиска живых строк
VACUUM users;
-- Обновляет карту видимости, помечая чистые блоки
Типы VACUUM
1. VACUUM обычный (Non-aggressive)
VACUUM users;
-- Удаляет только очень старые мёртвые строки
-- Использует xmin из committed transactions
-- Более быстрый, меньше блокирует
2. VACUUM FULL
VACUUM FULL users;
-- Полное сжатие таблицы
-- Перепаковывает блоки, перемещая живые строки
-- Освобождает место для ОС
-- ДОЛГИЙ и БЛОКИРУЕТ сильно (требует ACCESS EXCLUSIVE)
3. VACUUM ANALYZE
VACUUM ANALYZE users;
-- Вакуум + обновление статистики для плана запросов
-- Очень полезно после больших изменений данных
Автоматическое VACUUM (Autovacuum)
-- PostgreSQL автоматически запускает VACUUM
-- Параметры в postgresql.conf
autovacuum = on -- Включено по умолчанию
autovacuum_naptime = 1min -- Проверять каждую минуту
autovacuum_vacuum_threshold = 50 -- Если > 50 мёртвых строк
autovacuum_analyze_threshold = 50
-- Просмотр активных autovacuum процессов
SELECT * FROM pg_stat_activity WHERE query LIKE "%VACUUM%";
-- Статистика вакуума
SELECT relname, n_dead_tup, last_vacuum, last_autovacuum
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC;
Пример: мониторинг и запуск VACUUM
import psycopg2
from psycopg2.extras import execute_values
import time
def monitor_vacuum_status(conn):
"""Мониторит статус VACUUM"""
with conn.cursor() as cur:
cur.execute("""
SELECT
relname,
n_dead_tup,
n_live_tup,
round(100.0 * n_dead_tup / NULLIF(n_live_tup + n_dead_tup, 0), 2) as dead_ratio,
last_vacuum,
last_autovacuum
FROM pg_stat_user_tables
WHERE n_dead_tup > 1000 -- Много мёртвых строк
ORDER BY n_dead_tup DESC;
""")
results = cur.fetchall()
for table, dead, live, ratio, last_manual, last_auto in results:
print(f"{table}:")
print(f" Dead tuples: {dead}")
print(f" Live tuples: {live}")
print(f" Dead ratio: {ratio}%")
print(f" Last VACUUM: {last_manual}")
print(f" Last AUTOVACUUM: {last_auto}")
if ratio > 30: # Если более 30% мертво
print(f" -> Требуется явный VACUUM FULL")
def manual_vacuum(conn, table_name, full=False):
"""Запускает ручной VACUUM"""
with conn.cursor() as cur:
vacuum_cmd = "VACUUM FULL ANALYZE" if full else "VACUUM ANALYZE"
print(f"Запускаю: {vacuum_cmd} {table_name}")
start = time.time()
cur.execute(f"{vacuum_cmd} {table_name};")
conn.commit()
elapsed = time.time() - start
print(f"Завершено за {elapsed:.2f} сек")
# Проверяем результат
cur.execute("""
SELECT n_dead_tup, n_live_tup
FROM pg_stat_user_tables
WHERE relname = %s;
""", (table_name,))
dead, live = cur.fetchone()
print(f"После VACUUM: {dead} мёртвых, {live} живых строк")
# Использование
conn = psycopg2.connect("dbname=mydb user=postgres")
monitor_vacuum_status(conn)
manual_vacuum(conn, "users", full=False)
manual_vacuum(conn, "large_table", full=True)
Проблемы с VACUUM
1. VACUUM дольше обычно
-- Может быть, таблица очень большая или очень "грязная"
-- Решение: попытайтесь разделить на части
VACUUM (ANALYZE, VERBOSE) large_table; -- Медленно
-- Лучше:
-- 1. Увеличить работники: max_parallel_workers_per_gather
-- 2. Запустить в off-peak время
-- 3. Использовать VACUUM FULL только если критично
2. Autovacuum блокирует запросы
-- Autovacuum может блокировать SELECT/UPDATE
-- Решение:
-- Увеличить задержку autovacuum
autovacuum_vacuum_cost_delay = 50ms
autovacuum_vacuum_cost_limit = 1000 -- мс
-- Отключить autovacuum для таблицы во время peak
ALTER TABLE users SET (
autovacuum_enabled = false
);
-- Позже включить обратно
ALTER TABLE users SET (
autovacuum_enabled = true
);
3. Transaction ID wraparound
-- PostgreSQL использует 32-bit transaction IDs
-- После 2 млрд транзакций может наступить wraparound
-- VACUUM помогает избежать этого
-- Просмотр текущего txid
SELECT txid_current();
-- Если близко к wraparound — VACUUM FREEZE срочно
VACUUM FREEZE users; -- Помечает все строки как очень старые
Best Practices
-- 1. Оставить autovacuum включенным (default)
-- 2. Мониторить n_dead_tup
SELECT * FROM pg_stat_user_tables WHERE n_dead_tup > 10000;
-- 3. VACUUM FULL только когда критично
-- 4. Запускать во время низкой активности
-- 5. Для высоконагруженных таблиц настроить параметры
ALTER TABLE users SET (
autovacuum_vacuum_scale_factor = 0.01, -- 1% вместо 10%
autovacuum_analyze_scale_factor = 0.005 -- 0.5%
);
-- 6. Мониторить pg_stat_progress_vacuum (PG 13+)
SELECT * FROM pg_stat_progress_vacuum;
Сравнение операций
| Операция | Скорость | Блокировка | Использование | Когда |
|---|---|---|---|---|
| VACUUM | Быстро | Слабая | Частое | Автоматическое, регулярное |
| VACUUM FULL | Медленно | Сильная | Редкое | После больших удалений |
| VACUUM ANALYZE | Быстро | Слабая | Частое | После изменений данных |
| VACUUM FREEZE | Среднее | Средняя | Редкое | Близко к wraparound |
VACUUM — это жизненно важная часть администрирования PostgreSQL. Без неё таблицы разбухают и производительность падает.