Почему PostgreSQL иногда "зависает" на UPDATE и как это пофиксить

GuDron

dumpz.ws
Admin
Регистрация
28 Янв 2020
Сообщения
9,386
Реакции
1,547
Credits
32,796
Сегодня я расскажу про одну интересную особенность PostgreSQL, с которой сталкивался лично: внезапные подвисания при UPDATE большого количества строк. Причём CPU почти не загружен, а запрос как будто "висит".

Проблема часто кроется в отсутствии индекса на колонку фильтра в WHERE. Пример:
SQL:
UPDATE orders SET status = 'archived' WHERE created_at < '2022-01-01';

Если на created_at нет индекса, то PostgreSQL делает sequential scan всей таблицы. А теперь внимание: если в таблице много "мертвых" строк, которых ещё не убрал autovacuum, то PostgreSQL должен:

1. Прочитать кучу ненужных версий строк (MVCC).
2. Проверять видимость каждой строки.
3. Иногда ещё и ждать завершения других транзакций, держащих старые снапшоты.

Что делать:
- Проверить наличие индекса на колонку фильтра:
SQL:
CREATE INDEX idx_orders_created_at ON orders(created_at);

- Проверить состояние autovacuum:
SQL:
 SELECT relname, n_dead_tup, last_vacuum, last_autovacuum
 FROM pg_stat_user_tables ORDER BY n_dead_tup DESC;

- Можно вручную запустить:
SQL:
VACUUM ANALYZE orders;

Лайфхак: если UPDATE всё равно медленный, попробуй его разбить на батчи по 10 000 строк. Это снизит нагрузку и ускорит выполнение.