W powiązanej odpowiedzi, do której się odnosisz:
- AKTUALIZACJA Postgresa... LIMIT 1
Celem jest zablokowanie jednego wiersz na raz. Działa to dobrze z blokadami doradczymi lub bez nich, ponieważ nie ma szans na zakleszczenie - o ile nie próbujesz zablokować większej liczby wierszy w tej samej transakcji.
Twój przykład różni się tym, że chcesz zablokować 3000 wierszy naraz . Jest jest możliwość zakleszczenia, z wyjątkiem sytuacji, gdy wszystkie współbieżne operacje zapisu blokują wiersze w tej samej spójnej kolejności. Zgodnie z dokumentacją:
Najlepszą ochroną przed zakleszczeniami jest generalnie unikanie ich poprzez upewnienie się, że wszystkie aplikacje korzystające z bazy danych blokują wiele obiektów w spójnej kolejności.
Zaimplementuj to za pomocą ORDER BY w podzapytaniu.
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;
Jest to bezpieczne i niezawodne, o ile wszystkie transakcje uzyskują blokady w tej samej kolejności i nie należy oczekiwać współbieżnych aktualizacji kolumn porządkujących. (Przeczytaj żółte pole "UWAGA" na końcu tego rozdziału w podręczniku.) Powinno to być bezpieczne w twoim przypadku, ponieważ nie będziesz aktualizować id
kolumna.
W rzeczywistości tylko jeden klient naraz może w ten sposób manipulować wierszami. Transakcje współbieżne będą próbowały zablokować te same (zablokowane) wiersze i czekać na zakończenie pierwszej transakcji.
Zamki doradcze są przydatne, jeśli masz wiele lub bardzo długo działających jednoczesnych transakcji (wygląda na to, że nie masz). Mając tylko kilka, ogólnie taniej będzie po prostu użyć powyższego zapytania i pozwolić równoczesnym transakcjom czekać na swoją kolej.
Wszystko w jednym AKTUALIZACJA
Wygląda na to, że równoczesny dostęp sam w sobie nie stanowi problemu w Twojej konfiguracji. Współbieżność to problem stworzony przez Twoje obecne rozwiązanie.
Zamiast tego zrób to wszystko w jednej UPDATE
. Przypisz partie n
liczby (w przykładzie 3000) do każdego identyfikatora UUID i zaktualizuj wszystkie na raz. Powinien być najszybszy.
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;
Główne punkty
-
Obcinanie dzielenia liczb całkowitych. Otrzymasz 1 za pierwsze 3000 wierszy, 2 za następne 3000 wierszy. itp.
-
Wybieram wiersze arbitralnie, możesz zastosować
ORDER BY
w oknie dlarow_number()
aby przypisać określone wiersze. -
Jeśli nie masz tabeli identyfikatorów UUID do wysłania (
uuid_tbl
), użyjVALUES
wyrażenie, aby je dostarczyć. Przykład. -
Otrzymasz partie 3000 rzędów. W ostatniej partii zabraknie 3000, jeśli nie znajdziesz wielokrotności 3000 do przypisania.