V související odpovědi máte na mysli:
- AKTUALIZACE Postgresu... LIMIT 1
Cílem je zamknout jeden řádek najednou. Funguje to dobře s nebo bez poradních zámků, protože neexistuje žádná šance na uváznutí - pokud se nepokusíte uzamknout více řádků ve stejné transakci.
Váš příklad se liší v tom, že chcete uzamknout 3000 řádků najednou . existuje potenciální uváznutí, kromě případů, kdy všechny souběžné operace zápisu zamykají řádky ve stejném konzistentním pořadí. Podle dokumentace:
Nejlepší obranou proti uváznutí je obecně se jim vyhnout tím, že si budete jisti, že všechny aplikace používající databázi získávají zámky na více objektech v konzistentním pořadí.
Implementujte to pomocí příkazu ORDER BY ve vašem dílčím dotazu.
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;
To je bezpečné a spolehlivé, pokud vše transakce získávají zámky ve stejném pořadí a nelze očekávat souběžné aktualizace objednávkových sloupců. (Přečtěte si žluté pole „POZOR“ na konci této kapitoly v příručce.) Ve vašem případě by to tedy mělo být bezpečné, protože nebudete aktualizovat id
sloupec.
Tímto způsobem může efektivně manipulovat s řádky pouze jeden klient současně. Souběžné transakce by se pokusily uzamknout stejné (uzamčené) řádky a čekaly by na dokončení první transakce.
Poradní zámky jsou užitečné, pokud máte mnoho nebo velmi dlouho probíhajících souběžných transakcí (zdá se, že ne). Jen s několika bude celkově levnější použít výše uvedený dotaz a nechat souběžné transakce čekat, až na ně přijde řada.
AKTUALIZACE VŠE v jednom
Zdá se, že souběžný přístup nepředstavuje problém jako takový ve vašem nastavení. Souběžnost je problém vytvořený vaším aktuálním řešením.
Místo toho to udělejte vše v jediném UPDATE
. Přiřaďte dávky n
čísla (v příkladu 3000) ke každému UUID a aktualizujte je všechny najednou. Mělo by to být nejrychlejší.
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;
Hlavní body
-
Celočíselné dělení se zkracuje. Získáte 1 pro prvních 3000 řádků, 2 pro dalších 3000 řádků. atd.
-
Řádky vybírám libovolně, můžete použít
ORDER BY
v okně prorow_number()
přiřadit určité řádky. -
Pokud nemáte tabulku UUID k odeslání (
uuid_tbl
), použijteVALUES
výraz, který jim dodá. Příklad. -
Získáte dávky 3000 řádků. Pokud nenajdete násobek 3000 k přiřazení, bude poslední dávka kratší než 3000.