В свързания отговор имате предвид:
- Актуализиране на Postgres... ОГРАНИЧЕНИЕ 1
Целта е да се заключи един ред по ред. Това работи добре със или без съветни заключвания, защото няма няма шанс за блокиране - стига да не се опитвате да заключите повече редове в една и съща транзакция.
Вашият пример е различен по това, че искате да заключите 3000 реда наведнъж . Имаи потенциал за блокиране, освен ако всички едновременни операции на запис заключват редове в същия последователен ред. По документация:
Най-добрата защита срещу блокиране обикновено е да ги избягвате, като сте сигурни, че всички приложения, използващи база данни, придобиват заключвания върху множество обекти в последователен ред.
Приложете това с ORDER BY във вашата подзаявка.
UPDATE cargo_item item
SET job_id = 'SOME_UUID', job_ts = now()
FROM (
SELECT id
FROM cargo_item
WHERE state='NEW' AND job_id is null
ORDER BY id
LIMIT 3000
FOR UPDATE
) sub
WHERE item.id = sub.id;
Това е безопасно и надеждно, стига всички транзакциите придобиват заключвания в същия ред и не се очакват едновременни актуализации на колоните за поръчка. (Прочетете жълтото поле „ВНИМАНИЕ“ в края на тази глава в ръководството.) Така че това трябва да е безопасно във вашия случай, тъй като няма да актуализирате id
колона.
Ефективно само един клиент в даден момент може да манипулира редове по този начин. Едновременните транзакции ще се опитат да заключат същите (заключени) редове и да изчакат първата транзакция да завърши.
Съветни ключалки са полезни, ако имате много или много продължителни едновременни транзакции (изглежда, че нямате). Само с няколко, като цяло ще бъде по-евтино просто да използвате горната заявка и да накарате едновременните транзакции да чакат своя ред.
Всичко в едно АКТУАЛИЗИРАНЕ
Изглежда, че едновременният достъп не е проблем сам по себе си във вашата настройка. Паралелността е проблем, създаден от текущото ви решение.
Вместо това го направете всичко с една UPDATE
. Задайте партиди от n
номера (3000 в примера) за всеки UUID и актуализирайте всички наведнъж. Би трябвало да е най-бързо.
UPDATE cargo_item c
SET job_id = u.uuid_col
, job_ts = now()
FROM (
SELECT row_number() OVER () AS rn, uuid_col
FROM uuid_tbl WHERE <some_criteria> -- or see below
) u
JOIN (
SELECT (row_number() OVER () / 3000) + 1 AS rn, item.id
FROM cargo_item
WHERE state = 'NEW' AND job_id IS NULL
FOR UPDATE -- just to be sure
) c2 USING (rn)
WHERE c2.item_id = c.item_id;
Основни точки
-
Целочисленото деление се съкращава. Получавате 1 за първите 3000 реда, 2 за следващите 3000 реда. и др.
-
Избирам редове произволно, можете да приложите
ORDER BY
в прозореца заrow_number()
за присвояване на определени редове. -
Ако нямате таблица с UUID за изпращане (
uuid_tbl
), използвайтеVALUES
израз, който да им осигури. Пример. -
Получавате партиди от 3000 реда. Последната партида ще бъде под 3000, ако не намерите кратно на 3000 за присвояване.