Делает ли SELECT FOR UPDATE в PostgreSQL копирование записи в блоке данных?

Рейтинг: 1Ответов: 2Опубликовано: 26.04.2023

Известно, что PostgreSQL внутренне не обновляет записи -- при обновлении какой-либо записи просто предыдущая версия помечается устаревшей и в блок данных добавляется копия этой записи. При этом записей в блоке ограничено размером этого блока, а старые записи удаляются только VACUUM-ом. Если блок переполняется, то нужно писать в новый, а это потеря производительности, и/или еще какие-то минусы.

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

Архитектура PostgreSQL приведет к тому, что на каждую транзакцию обновления будет копироваться 90% не меняющихся полей в таблице ради того, чтобы записать оставшиеся 10%. Это приведет к быстрому распуханию файлов данных.

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

Приложение в своей работе использует SELECT FOR UPDATE на этой таблице, чтобы бизнес-запросы сериализовались в правильном порядке. Когда мы разделим таблицу на две, то SELECT FOR UPDATE будет делаться только для одной из таблиц (это делает ORM, на который не повлиять, вторая таблица все равно не будет использоваться без первой), и тут возникает вопрос, а чем является этот SELECT FOR UPDATE? Является ли он той записью (write), которая приводит к дублированию записи (row/record) в файлах данных или нет?

Документация для SELECT FOR UPDATE направляет на документацию по блокировкам, где сказано следующее

PostgreSQL doesn't remember any information about modified rows in memory, so there is no limit on the number of rows locked at one time. However, locking a row might cause a disk write, e.g., SELECT FOR UPDATE modifies selected rows to mark them locked, and so will result in disk writes.

Означает ли выделенное то самое копирование строчки (row/record) в блоке данных? Иными словами, ради того, чтобы разделение большой таблицы имело задуманный смысл, нужно ли делать SELECT FOR UPDATE для таблицы с меньшим числом полей (оперативные данные), или такая оптимизация также сработает, если будет делаться для таблицы с большим числом полей (настроечные данные)?

Ответы

▲ 1Принят
  • select for update производит модификацию данных страницы, содержащей эту строку
  • select for update не записывает новую копию строки = не приводит к раздуванию таблицы

Оба утверждения верны.

И отсюда нужно пояснять, как именно работает блокировка строки на уровне реализации. Общий обзор приведён в src/backend/access/heap/README.tuplock, я процитирую только самое необходимое:

is implemented by storing locking information in the tuple header: a tuple is marked as locked by setting the current transaction's XID as its XMAX, and setting additional infomask bits to distinguish this case from the more normal case of having deleted the tuple

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

Во время этого процесса мы меняем заголовок страницы именно там где строка расположена на странице данных - это обычная запись данных, страничка "пачкается", пишется на диск и так далее.

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

▲ 0

SELECT FOR UPDATE в PostgreSQL блокирует выбранные строки для изменения другими транзакциями до завершения текущей транзакции. Он не копирует записи в блоке данных.

SELECT FOR UPDATE блокирует выбранные строки с помощью механизма блокировки строк в PostgreSQL. Это означает, что другие транзакции не смогут изменять заблокированные строки до тех пор, пока текущая транзакция не завершится.

Таким образом, SELECT FOR UPDATE не создает никаких дополнительных копий записей в блоке данных. Он просто блокирует выбранные строки для изменения другими транзакциями до завершения текущей транзакции.