Добавление обнуляемого столбца в таблицу стоит более 10 минут

11

У меня проблемы с добавлением нового столбца в таблицу.
Я пытался запустить его пару раз, но после более 10 минут работы решил отменить запрос из-за времени блокировки.

ALTER TABLE mytable ADD mycolumn VARCHAR(50);

Полезная информация:

  • Версия PostgreSQL: 9.1
  • Количество рядов: ~ 250К
  • Количество столбцов: 38
  • Количество обнуляемых столбцов: 32
  • Количество ограничений: 5 (1 ПК, 3 ФК, 1 УНИКАЛЬНЫЙ)
  • Количество индексов: 1
  • Тип ОС: Debian Squeeze 64

Я нашел интересную информацию о том, как PostgreSQL управляет обнуляемыми столбцами (через HeapTupleHeader).

Мое первое предположение состоит в том, что, поскольку в этой таблице уже есть 32 столбца с 8-разрядными значениями MAXALIGN, которые можно обнулять, длина HeapTupleHeader составляет 4 байта (не проверено, и я не знаю, как это сделать).

Таким образом, добавление нового столбца, который может содержать пустые значения, может потребовать обновления HeapTupleHeader в каждой строке, чтобы добавить новые 8-битные значения MAXALIGN, что может вызвать проблемы с производительностью.

Поэтому я попытался изменить один из столбцов, допускающих значение NULL, (которые на самом деле не могут иметь значение NULL), чтобы уменьшить количество столбцов, которые могут быть NULL, до 31, чтобы проверить, может ли мое предположение быть верным.

ALTER TABLE mytable ALTER myothercolumn SET NOT NULL;

К сожалению, это изменение также занимает очень много времени, более 5 минут, поэтому я также прервал его.

У вас есть представление о том, что может привести к снижению производительности?

Матье Верреккья
источник
1
Ну, я могу рассказать вам об этом: изменение типа столбца на другой тип, который не является двоично-совместимым, фактически создает новый столбец, копирует данные и устанавливает старый столбец как удаленный. Тем SET NOT NULLне менее, не изменяет тип, он просто добавляет ограничение - но ограничение должно быть проверено по таблице, а для этого требуется полное сканирование таблицы. 9.4 улучшает некоторые из этих случаев, используя более слабые блокировки, но все еще довольно тяжелый.
Крейг Рингер
1
Прежде чем заподозрить, что он работает медленно, вы должны убедиться, что ALTER TABLE не просто ожидает блокировки. Укажите это в вопросе, если вы проверили.
Даниэль Верите
Спасибо Крейг и Дэниел. Когда я запускаю команду alter, она появляется в pg_stat_activity с ожиданием "true", я полагаю, это означает, что она ожидает блокировки!? Это хороший способ проверки? Кстати, перед запуском этого альтера все идет хорошо, но через несколько секунд после старта количество блокировок растет
Попробуйте запрос на wiki.postgresql.org/wiki/Lock_dependency_information для лучшего просмотра. Либо у вас есть длительные транзакции, которые забывают зафиксировать, либо активная работа с этой таблицей, которая всегда занята.
Даниэль Верите
Может быть лучше подойдет на dba.SE.
Эрвин Брандштеттер

Ответы:

8

Здесь есть несколько недоразумений:

Нуль растровый является не частью заголовка кучи кортежа. По документации:

Существует заголовок фиксированного размера (занимающий 23 байта на большинстве машин), за которым следует необязательный нулевой битовый массив ...

Ваши 32 обнуляемых столбцов не подозрительны по двум причинам:

  • Пустое растровое изображение добавляется в строку и только в том случае, если в строке есть хотя бы одно фактическое NULLзначение . Обнуляемые столбцы не имеют прямого влияния, только фактические NULLзначения. Если нулевое растровое изображение выделено, оно всегда выделяется полностью (все или ничего). Фактический размер нулевого растрового изображения составляет 1 бит на столбец, округленный до следующего байта . По текущему коду

    #define BITMAPLEN(NATTS) (((int)(NATTS) + 7) / 8)
  • Нулевое растровое изображение выделяется после заголовка кортежа кучи, за которым следует необязательный OID, а затем данные строки. Начало OID или данных строки указано t_hoffв заголовке. За исходный код комментария :

    Обратите внимание, что t_hoff должен быть кратным MAXALIGN.

  • После заголовка кортежа кучи есть один свободный байт, который занимает 23 байта. Таким образом, нулевое растровое изображение для строк до 8 столбцов эффективно предоставляется без дополнительных затрат. С 9-м столбцом в таблице t_hoffдобавляются еще один MAXALIGN(обычно 8) байтов, чтобы обеспечить еще 64 столбца. Таким образом, следующая граница будет в 72 столбцах.

Чтобы отобразить управляющую информацию кластера базы данных PostgreSQL (вкл. MAXALIGN), Пример типичной установки Postgres 9.3 на компьютере Debian:

    sudo /usr/lib/postgresql/9.3/bin/pg_controldata /var/lib/postgresql/9.3/main

Я обновил инструкции в соответствующем ответе, который вы цитировали .

Помимо всего этого, даже если ваш ALTER TABLEоператор вызывает перезапись всей таблицы (что, вероятно, и происходит, изменяя тип данных), 250 КБ - это на самом деле не так много, и это будет считаться секундами на любой приличной машине (если строки не слишком большие) , 10 и более минут указывают на совершенно другую проблему. Ваше заявление ожидает блокировки на столе, скорее всего.

Растущее число записей в pg_stat_activityозначает больше открытых транзакций - указывает на одновременный доступ к таблице (скорее всего), который должен ждать завершения операции.

Несколько снимков в темноте

Проверьте наличие возможного раздувания таблиц, попробуйте осторожный VACUUM mytableили более агрессивный VACUUM FULL mytable- который может столкнуться с теми же проблемами параллелизма, поскольку эта форма также получает эксклюзивную блокировку. Вместо этого вы можете попробовать pg_repack ...

Я бы начал с изучения возможных проблем с индексами, триггерами, внешним ключом или другими ограничениями, особенно теми, которые касаются столбца. Особенно поврежденный индекс может быть вовлечен? Попробуйте REINDEX TABLE mytable;или DROPвсе из них и повторно добавьте их после ALTER TABLE в той же транзакции .

Попробуйте выполнить команду ночью или всякий раз, когда нет большой нагрузки.

Методом грубой силы будет остановить доступ к серверу, а затем повторите попытку:

Не имея возможности определить это, обновление до текущей версии или, в частности, до 9.4 может помочь. Было несколько улучшений для больших таблиц и для блокировки деталей. Но если в вашей БД что-то сломано, вам, вероятно, следует сначала это выяснить.

Эрвин Брандштеттер
источник
2
Это почти наверняка замки. Но, как тест, вы всегда можете создать копию таблицы и попробовать ее изменить. Если это не займет много времени, то вы знаете, что проблема не в реальной модификации.
Спасибо за объяснения, Эрвин. Я думаю, что вы правы, похоже, проблема с блокировкой. Когда я проверяю pg_stat_activity, я вижу, что мой ALTER имеет значение "ожидание". Я не могу понять, почему ALTER не может получить блокировку таблицы, потому что даже когда я не могу найти ни одного запущенного запроса, кажется, что он не может его получить. Но как только мой ALTER начинает работать, все остальные запросы ждут его завершения. Таким образом, активность указывает на то, что ALTER блокирует все остальные запросы, но также указывает на то, что ALTER не получил блокировку. Я думаю, что есть что-то, что я не очень хорошо понимаю!
@MatthieuVerrecchia: Вы пробовали тест, предложенный Ричардом?
Эрвин Брандштеттер,
1
Я просто клонировал свою таблицу на новую (с помощью pg_dump -> pg_sql). Новый столбец правильно добавлен через 50 мс, что подтверждает проблему блокировки. Кстати, до сих пор не понимаю, почему ALTER не может получить блокировку с действительно стандартной активностью БД.
1
@ErwinBrandstetter Я следовал вашим советам и пробовал ВАКУУМ, а затем РЕИНДЕКС. REINDEX также блокировал, потому что он также не смог получить блокировку. После некоторых исследований проблема оказалась проще, чем мы думали. Оставалась неделя <IDLE> с открытой транзакцией. Проблема решена, спасибо для всего, информация была очень полезна.