Если задать вопрос: "Что блокируется при UPDATE строки или таблица?", думаю что большинство разработчиков не задумываясь ответит — строки. Но не всё так однозначно, как хотелось бы. Ниже мы разберёмся почему PostgreSQL предпочитает блокировать строки и в каких случаях обновление может заблокировать почти всю таблицу.
Что такое блокировки
Для полного рассказа о блокировках понадобится отдельная статья. Здесь я вкратце поясню про них для тех кто забыл, или не знал
Блокировки — это инструмент базы данных для сохранения целостности данных. Чтобы разные транзакции не мешали друг другу работать с данными, СУБД может заблокировать или строки или всю таблицу целиком для одной из них, и тогда остальные будут ждать пока блокировка снимется
Почему UPDATE выбирает строки, а не всю таблицу
В большинстве случаев при UPDATE указываются условия по которым нужно обновить данные
(WHERE). PostgreSQL смотрит на условие, находит нужные строки и блокирует только их. Остальные данные
остаются доступны для других запросов.
-- Возьмём таблицу table_name с первичным ключом id:
UPDATE table_name
SET name = 'new_name'
WHERE id = 777;
-- PostgreSQL находит по ключу строку с id = 777 -> блокирует её -> создаёт новую версию строки
-- с обновлённым значением -> снимает блокировку (блокировка держится до конца транзакции, это важно учитывать если в транзакции есть другие запросы)
-- Возьмём таблицу table_name_2 с индексом на столбцах time_create, active
UPDATE table_name_2
SET active = 0
WHERE time_create <= '2026-01-01'
AND active = 1;
-- Если планировщик использует индекс (time_create, active), база находит подходящие строки -> блокирует их -> обновляет
-- -> снимает блокировки (блокировки держатся до конца транзакции)
Важно понимать, что в PostgreSQL обновление работает через MVCC: при UPDATE не изменяется строка, а
создаётся её новая версия. Блокировка нужна только для того, чтобы две транзакции одновременно не изменили одну
и ту же строку.
Почему при UPDATE может казаться, что заблокирована вся таблица
-- Возьмём таблицу table_name с первичным ключом id:
UPDATE table_name
SET active = 0
WHERE name ~* 'search_name';
-- Для того чтобы найти строки удовлетворяющие условию PostgreSQL нужно просканировать всю таблицу:
-- База читает строки одну за другой -> как только строка подходит под условие, блокирует её -> обновляет
-- Остальные строки при этом не блокируются
На практике такие ситуации чаще всего связаны не с типом блокировки, а с количеством затронутых строк. Вот основные
причины по которым при UPDATE может создаваться ощущение, что заблокирована вся таблица:
- Отсутствие индекса по полю(ям) в условии — в этом случае PostgreSQL приходится сканировать всю таблицу, при последовательном сканировании запрос может “упираться” в уже заблокированные строки и ждать, из-за чего создаётся эффект полной блокировки таблицы
- Слишком обширное условие
WHERE— если под него попадает большая часть таблицы (80–90% строк), будет заблокировано почти столько же строк. В итоге для других транзакций это может выглядеть как «заблокирована вся таблица», хотя технически блокировки остаются построчными
Полезные советы:
- Лучше добавлять индексы на поля которые часто используются в условиях
UPDATE. Это ускорит поиск и позволит уменьшить область блокировки - Если есть возможность, то стоит разбивать массовые
UPDATEна несколько партий. За счёт этого можно снизить нагрузку и время блокировки - Проверяйте новые запросы с помощью
EXPLAIN(а лучшеEXPLAIN ANALYZE, но только на тестовой базе, т.к.ANALYZEвыполнит запрос) — так можно узнать сколько строк затронетUPDATEи использует ли он индексы