Многоверсионность
Очистка
13
Авторские права
© Postgres Professional, 2016–2022.
Авторы: Егор Рогов, Павел Лузанов, Илья Баштанов
Использование материалов курса
Некоммерческое использование материалов курса (презентации,
демонстрации) разрешается без ограничений. Коммерческое
использование возможно только с письменного разрешения компании
Postgres Professional. Запрещается внесение изменений в материалы
курса.
Обратная связь
Отзывы, замечания и предложения направляйте по адресу:
Отказ от ответственности
Компания Postgres Professional не несет никакой ответственности за
любые повреждения и убытки, включая потерю дохода, нанесенные
прямым или непрямым, специальным или случайным использованием
материалов курса. Компания Postgres Professional не предоставляет
каких-либо гарантий на материалы курса. Материалы курса
предоставляются на основе принципа «как есть» и компания Postgres
Professional не обязана предоставлять сопровождение, поддержку,
обновления, расширения и изменения.
2
Темы
Обычная очистка и схема ее работы
Регулирование нагрузки
Анализ
Полная очистка и аналоги
3
Обычная очистка
Выполняется командой VACUUM
не конфликтует с обычной активностью в системе
Обрабатывает таблицу и все ее индексы
очищает ненужные версии строк в табличных страницах
(пропуская страницы, уже отмеченные в карте видимости)
очищает индексные записи, ссылающиеся на очищенные версии строк
освобождает указатели
обновляет карту свободного пространства
обновляет карту видимости
Внутристраничная очистка выполняется быстро, но не решает всех
задач. Она работает только в пределах одной табличной страницы и не
затрагивает индексы.
Очистка, выполняемая командой VACUUM, обрабатывает таблицу
полностью, включая все созданные на ней индексы, освобождая место
за счет удаления и ненужных версий строк, и указателей.
Обработка происходит в фоновом режиме, таблица при этом может
использоваться обычным образом и для чтения, и для изменения
днако одновременное выполнение для таблицы таких команд, как
CREATE INDEX, ALTER TABLE и др. будет невозможно — подробнее
см. модуль «Блокировки»).
Из таблицы читаются только те страницы, в которых происходила
какая-то активность. Для этого используется карта видимости (visibility
map), в которой отмечены страницы, содержащие только достаточно
старые версии строк, которые гарантированно видимы во всех снимках.
Обрабатываются только страницы, не отмеченные в карте, а сама
карта при этом обновляется.
В процессе работы обновляется и карта свободного пространства (free
space map), в которой отражается наличие свободного места
в страницах.
4
До очистки
xmin committed
xmin aborted
xmax committed
xmax aborted
xmin данные
ctid ключ
(0,1)
статус
normal
(0,2) BAR
normal(0,2)
(0,3) BAZ
xmax
heap hot upd
heap only tuple
normal(0,3)
(0,1) FOO
t100 t 42,FOO101
t101 t 42,BAR102
t102 42,BAZ
На рисунке приведена ситуация до очистки. В табличной странице три
версии одной строки. Две из них неактуальны, не видны ни в одном
снимке и могут быть удалены.
В индексе есть три ссылки на каждую из версий строки.
5
1. Сканирование таблицы
xmin committed
xmin aborted
xmax committed
xmax aborted
xmin данные
ctid ключ
(0,1)
статус
(0,2) BAR
(0,2)
(0,3) BAZ
xmax
heap hot upd
heap only tuple
(0,3)
(0,1) FOO
(0,1)
(0,2)
maintenance_work_mem
идентификаторы
очищаемых версий
строк
normal
normal
normal
t100 t 42,FOO101
t101 t 42,BAR102
t102 42,BAZ
Сначала VACUUM выполняет сканирование таблицы (пропуская
те страницы, которые помечены в карте видимости).
В прочитанных страницах процесс определяет ненужные версии строк
и записывает их идентификаторы в специальный массив. Для этого
в локальной памяти процесса VACUUM выделяется фрагмент
размером maintenance_work_mem (или меньше, если таблица
небольшая). Значение этого параметра по умолчанию — 64 Мбайт.
Отметим, что эта память выделяется сразу в полном объеме, а не по
мере необходимости.
6
2. Очистка индексов
xmin committed
xmin aborted
xmax committed
xmax aborted
xmin данные
ctid ключ
(0,1)
статус
(0,2)
xmax
heap hot upd
heap only tuple
(0,3)
(0,1)
(0,2)
maintenance_work_mem
(0,3) BAZ
требуется
полное сканирование
индекса
normal
normal
normal
t100 t 42,FOO101
t101 t 42,BAR102
t102 42,BAZ
Когда выделенная под массив память заканчивается (или если мы уже
дошли до конца таблицы), выполняется очистка индексов.
Для этого каждый из индексов, созданных на таблице, полностью
сканируется в поисках записей, которые ссылаются на очищаемые
версии строк. Найденные записи очищаются из индексных страниц.
7
3. Очистка таблицы
xmin committed
xmin aborted
xmax committed
xmax aborted
xmin данные
ctid ключ
(0,1)
статус
(0,2)
xmax
heap hot upd
heap only tuple
normal(0,3) t102 42,BAZ
maintenance_work_mem
(0,3) BAZ
unused
unused
память
очищена и готова
к продолжению
После этого необходимые табличные страницы повторно читаются,
чтобы очистить в них ненужные версии строк и освободить указатели,
на которые больше нет ссылок из индексов.
Если на первом проходе таблица не была просканирована полностью,
VACUUM очищает массив в памяти и продолжает работу с этой точки.
Таким образом, если при очистке удаляется так много версий строк, что
их идентификаторы не помещаются в память размером
maintenance_work_mem, все индексы будут полностью сканироваться
несколько раз.
На больших таблицах это может занимать существенное время
и создавать существенную нагрузку на систему. Чтобы ускорить
процесс, имеет смысл либо вызывать очистку чаще (чтобы за каждый
раз очищалось не очень большое количество версий строк), либо
выделить больше памяти.
В конце работы очистки выполняется этап усечения таблицы. Если
в конце файла образовалось достаточно много пустых страниц, они
«откусываются» и освободившееся место возвращается операционной
системе. Это действие требует установки исключительной блокировки.
Если блокировка вызывает проблемы, этап усечения можно отключить
параметром хранения vacuum_truncate или явно: VACUUM (TRUNCATE
off). Подробнее см. модуль «Блокировки».
8
Мониторинг
VACUUM VERBOSE
Представление pg_stat_progress_vacuum
полный размер таблицы
число прочитанных страниц и число очищенных страниц
количество уже завершенных циклов очистки индексов
число идентификаторов версий строк, помещающихся в память,
и текущее число идентификаторов в памяти
текущая фаза очистки
Если очистка выполняется долго, может потребоваться узнать текущее
состояние дел.
Для этого можно вызывать VACUUM с указанием VERBOSE — на
консоль будет выводиться информация о ходе выполнения.
Кроме того, есть представление pg_stat_progress_vacuum, которое
содержит всю информацию о выполняющейся в данный момент
очистке. Ход очистки таблицы можно отслеживать, сравнивая число
очищенных страниц (heap_blks_vacuumed) с общим количеством
(heap_blks_total). Ход очистки индексов детально не отображается,
но основное внимание надо обращать на количество циклов очистки
(index_vacuum_count) — значение больше 1 означает, что памяти
maintenance_work_mem не хватило для того, чтобы завершить очистку
за один проход.
10
Регулирование нагрузки
Процесс чередует работу и ожидание
примерно vacuum_cost_limit условных единиц работы,
затем засыпает на vacuum_cost_delay мс
Настройки
vacuum_cost_limit = 200
vacuum_cost_delay = 0 ms
стоимость обработки:
vacuum_cost_page_hit = 1 — страница нашлась в кеше
vacuum_cost_page_miss = 10 — страница прочитана с диска
vacuum_cost_page_dirty = 20 — чистая страница стала грязной
Поскольку процесс очистки интенсивно работает с таблицами
и индексами, может оказаться необходимым распределить действия
во времени и тем самым сгладить пики нагрузки. Для этого существует
возможность выполнять очистку порциями, чередуя работу и ожидание.
Размер порции vacuum_cost_limit указывается в условных единицах.
Три других параметра, указанных на слайде, определяют стоимость
обработки одной страницы в этих единицах в зависимости от
необходимости разных действий. В лучшем случае стоимость будет
равна vacuum_cost_hit (если страница нашлась в кеше и либо не
изменилась, либо уже была грязной); в худшем случае —
vacuum_cost_page_miss + vacuum_cost_page_dirty (если страница была
прочитана с диска и изменилась в результате очистки).
Настройка по умолчанию фактически отключает механизм
регулирования нагрузки, так как время ожидания выставлено в ноль.
Это сделано из тех соображений, что если администратору пришлось
запускать VACUUM вручную, он, скорее всего, хочет выполнить очистку
как можно быстрее.
11
Параллельная очистка
Сканирование и очистка таблицы — последовательно
Очистка индексов может выполняться параллельно
каждый индекс — только одним рабочим процессом
размер индекса должен превышать min_parallel_index_scan_size
количество процессов ограничено max_parallel_maintenance_workers
и, если указано, значением VACUUM (PARALLEL n)
Этапы сканирования и очистки таблицы всегда выполняются
последовательно одним процессом.
Этап очистки индексов может выполняться в параллельном режиме.
Это происходит, если на таблице создано несколько (больше одного)
достаточно больших индексов: размер индекса должен превышать
значение параметра min_parallel_index_scan_size (512 Кбайт по
умолчанию). Тогда для каждого подходящего индекса запускается
отдельный рабочий процесс.
Каждый индекс обрабатывается только одним рабочим процессом,
то есть несколько процессов не могут очищать один и тот же индекс.
Количество процессов ограничено сверху значением параметра
max_parallel_maintenance_workers и может быть дополнительно
ограничено явным указанием степени параллелизма при вызове
команды VACUUM (PARALLEL n).
12
Анализ
Выполняется при VACUUM ANALYZE или ANALYZE
не конфликтует с обычной активностью в системе
Собирает статистику для планировщика
Еще одна задача, которую обычно совмещают с очисткой, — анализ,
то есть сбор статистической информации для планировщика запросов.
Анализируется число строк в таблице, распределение данных по
столбцам и т. п. Более подробно это рассматривается в курсе QPT.
Вручную анализ выполняется командой ANALYZE (только анализ) или
VACUUM ANALYZE (и очистка, и анализ).
Как и с обычной очисткой, обработка происходит в фоновом режиме
и не мешает обычному использованию таблиц и индексов.
14
Полная очистка
Выполняется командой VACUUM FULL
не совместима ни с какими операциями над таблицей, включая чтение
Полностью перестраивает таблицу и все ее индексы
при работе потребуется дополнительное место для новых файлов
освобожденное место возвращается операционной системе
выполняется дольше, чем обычная очистка
Обычная очистка не решает всех задач по освобождению места.
Если таблица или индекс сильно выросли в размерах, то очистка не
приведет к сокращению числа страниц (и к уменьшению файлов).
Вместо этого внутри существующих страниц появятся «дыры», которые
могут быть использованы для вставки новых строк или изменения
существующих. Единственное исключение составляют полностью
очищенные страницы, находящиеся в конце файла — такие страницы
«откусываются» и возвращаются операционной системе.
Если размер файлов превышает некие разумные пределы, можно
выполнить полную очистку. При этом таблица и все ее индексы
перестраиваются полностью с нуля, а данные упаковываются
максимально компактно (разумеется, с учетом fillfactor).
При перестройке PostgreSQL последовательно перестраивает сначала
таблицу, а затем и каждый из индексов. Для них создаются новые
файлы, а старые удаляются. Следует учитывать, что в процессе работы
на диске потребуется дополнительное место.
Полная очистка не предполагает регулярного использования, так как
полностью блокирует всякую работу с таблицей (включая и выполнение
запросов к ней) на все время своей работы. На активно используемой
системе это может быть неприемлемым; в таком случае может помочь
расширение pg_repack (https://github.com/reorg/pg_repack), не входящее
в поставку.
15
До полной очистки
xmin committed
xmin aborted
xmax committed
xmax aborted
xmin данные
ctid ключ
(0,1)
статус
normal
(0,2) BAR
normal(0,2)
(0,3) BAZ
xmax
heap hot upd
heap only tuple
normal(0,3)
(0,1) FOO
t100 t 42,FOO101
t101 t 42,BAR102
t102 42,BAZ
На рисунке приведена ситуация до очистки. В табличной странице три
версии одной строки. Две из них неактуальны, не видны ни в одном
снимке и могут быть удалены.
В индексе есть три ссылки на каждую из версий строки.
16
normal
После полной очистки
ctid ключ
(0,1)
статус
(0,1) BAZ
xmin committed
xmin aborted
xmax committed
xmax aborted
xmin данныеxmax
heap hot upd
heap only tuple
t102 42,BAZ
После выполнения полной очистки содержимое таблицы и индекса
полностью перестроено и физически находится в других файлах.
При этом OID таблицы в системном каталоге сохраняется.
18
Аналоги полной очистки
CLUSTER
полностью перестраивает таблицу и все ее индексы
дополнительно физически упорядочивает версии строк
в соответствии с одним из индексов
REINDEX
полностью перестраивает отдельный индекс
TRUNCATE
«опустошает» таблицу
Особенности
все команды полностью блокируют работу с таблицей
все команды создают новые файлы для данных
Есть несколько команд, которые работают, используя схожий механизм.
Все они полностью блокируют работу с таблицей, все они удаляют
старые файлы данных и создают новые.
Команда CLUSTER во всем аналогична VACUUM FULL, но
дополнительно физически упорядочивает версии строк в соответствии
с одним из индексов. Это дает планировщику возможность более
эффективно использовать индексный доступ в некоторых случаях.
Однако надо понимать, что кластеризация не поддерживается: при
последующих изменениях таблицы физический порядок версий строк
будет нарушаться.
Команда REINDEX перестраивает отдельный индекс на таблице.
Фактически, VACUUM FULL и CLUSTER используют эту команду для
того, чтобы перестроить индексы.
Команда TRUNCATE логически работает так же, как и DELETE
удаляет все табличные строки. Но DELETE, как уже было рассмотрено,
помечает версии строк как удаленные, что требует дальнейшей
очистки. TRUNCATE же просто создает новый, чистый файл. Это
работает быстрее, но надо учитывать, что TRUNCATE заблокирует
работу с таблицей на все время до конца транзакции.
19
Итоги
Обычная очистка освобождает место в страницах
выполняется в фоновом режиме
может потребоваться несколько раз сканировать индексы
Анализ собирает статистику для планировщика
выполняется в фоновом режиме
можно совместить с очисткой
Полная очистка перестраивает таблицу и индексы
блокирует работу с таблицей на все время работы
20
Практика
1. Создайте большую таблицу с индексом. Временно
уменьшите значение maintenance_work_mem так, чтобы
потребовалось несколько проходов для очистки индекса.
Проконтролируйте, запуская VACUUM VERBOSE.
2. Удалите из большой таблицы 90% случайных строк
и проверьте, как изменился объем, который таблица
занимает на диске, после выполнения обычной очистки.
3. Повторите п. 2 с полной очисткой.
1. Чтобы исключить срабатывание автоматической очистки
(рассматривается в следующей теме), при создании таблицы укажите
параметр хранения autovacuum_enabled:
CREATE TABLE ... WITH (autovacuum_enabled = off);