Postgresql Alter Table замораживает БД (некоторые загружают процессор) - PullRequest
3 голосов
/ 10 апреля 2020

PostgreSQL 9,6

  1. Я делаю

    ALTER TABLE "Users"
       ADD COLUMN "someField" BOOLEAN NULL DEFAULT NULL;
    
  2. DB останавливается на 10+ минут.

  3. Таблица "Users" имеет 5000 строк, но база данных очень большая (150+ Гб).

  4. Сразу же более 20+ SELECT запросов пользователей отображение таблицы, (проверено с помощью):

    SELECT query FROM pg_stat_activity
    WHERE state = 'active' and query LIKE 'SELECT%'
    

    (раньше запросов не было)

  5. Эти SELECT запросы занимают весь ЦП.

  6. Я попытался перезапустить базу данных и попытался сделать VACUUM ANALYZE для таблицы "Users":

INFO:  vacuuming "public.Users"
INFO:  index "Users_pkey" now contains 5556 row versions in 86 pages
DETAIL:  0 index row versions were removed.
1 index pages have been deleted, 1 are currently reusable.
CPU 0.00s/0.00u sec elapsed 0.12 sec.
INFO:  index "users_subscription_canceled" now contains 5028 row versions in 508 pages
DETAIL:  0 index row versions were removed.
8 index pages have been deleted, 8 are currently reusable.
CPU 0.00s/0.00u sec elapsed 1.54 sec.
INFO:  index "users_shopper_id" now contains 5556 row versions in 205 pages
DETAIL:  0 index row versions were removed.
1 index pages have been deleted, 1 are currently reusable.
CPU 0.00s/0.00u sec elapsed 0.64 sec.
INFO:  index "users_referrer" now contains 5556 row versions in 586 pages
DETAIL:  0 index row versions were removed.
4 index pages have been deleted, 4 are currently reusable.
CPU 0.00s/0.00u sec elapsed 1.80 sec.
INFO:  index "users_referral_code" now contains 5556 row versions in 84 pages
DETAIL:  0 index row versions were removed.
2 index pages have been deleted, 2 are currently reusable.
CPU 0.00s/0.00u sec elapsed 0.27 sec.
INFO:  "Users": found 0 removable, 2137 nonremovable row versions in 803 out of 2780 pages
DETAIL:  445 dead row versions cannot be removed yet.
There were 25647 unused item pointers.
Skipped 0 pages due to buffer pins.
0 pages are entirely empty.
CPU 0.00s/0.00u sec elapsed 6.91 sec.
INFO:  "Users": stopping truncate due to conflicting lock request
INFO:  vacuuming "pg_toast.pg_toast_26460"
INFO:  index "pg_toast_26460_index" now contains 0 row versions in 1 pages
DETAIL:  0 index row versions were removed.
0 index pages have been deleted, 0 are currently reusable.
CPU 0.00s/0.00u sec elapsed 0.00 sec.
INFO:  "pg_toast_26460": found 0 removable, 0 nonremovable row versions in 0 out of 0 pages
DETAIL:  0 dead row versions cannot be removed yet.
There were 0 unused item pointers.
Skipped 0 pages due to buffer pins.
0 pages are entirely empty.
CPU 0.00s/0.00u sec elapsed 0.00 sec.
INFO:  analyzing "public.Users"
INFO:  "Users": scanned 2780 of 2780 pages, containing 5539 live rows and 459 dead rows; 5539 rows in sample, 5539 estimated total rows
VACUUM
Если я сделаю DROP COLUMN или RENAME COLUMN, то же самое произойдет с пунктами 4 и 5 - и база данных зависнет.

Вопросы:

  1. Это какая-то ошибка с базой данных? Добавление поля Nullable должно быть очень быстрым.

  2. Любые рекомендации действительно приветствуются, я устал от поиска в Google и отладки:)

1 Ответ

2 голосов
/ 10 апреля 2020

Это нормальное поведение; проблема в вашей рабочей нагрузке.

Вы правы, что ALTER TABLE ... ADD COLUMN - это очень быстрая операция. Это не проблема. Проблема в том, что для такого ALTER TABLE нужна короткая блокировка ACCESS EXCLUSIVE на таблице, поскольку она изменяет структуру таблицы.

Такая блокировка ACCESS EXCLUSIVE не совместима с блокировкой ACCESS SHARE, что SELECT оператор ставит на стол. Вот почему: как должен вести себя оператор SELECT, если таблица изменяется во время ее работы?

Теперь проблема в том, что либо ваши запросы занимают много времени, либо кто-то забыл закрыть транзакцию, которая имеет блокировка на столе.

Вы можете проверить это с помощью

SELECT pid, a.state, a.xact_start
FROM pg_locks AS l
   JOIN pg_stat_activity AS a USING (pid)
WHERE l.relation = 'Users'::regclass;

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

Сейчас ваш ALTER TABLE должен ждать, пока все эти транзакции не будут выполнены, и все короткие операторы SELECT, которые будут выданы позже, должны стоять в очереди за ALTER TABLE.

, как только ALTER TABLE получит заблокировать это нужно, это будет сделано очень быстро и снять блокировку на столе. Теперь все остальные операторы, находящиеся в очереди, будут одновременно освобождены и создадут высокую нагрузку на ваш компьютер.

Решение состоит из двух частей:

  1. Исправьте приложение так, чтобы оно сразу закрывало транзакции.

  2. Максимально уменьшите max_connections, используя пул соединений. Тогда количество операторов, которые можно заблокировать, будет ограничено, и опасность перегрузки машины станет меньше.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...