Vámi uvedené záruky platí v tomto jednoduchém případě, ale ne nutně v trochu složitějších dotazech. Příklady viz konec odpovědi.
Jednoduchý případ
Za předpokladu, že col1 je jedinečný, má přesně jednu hodnotu "2" nebo má stabilní řazení, takže každý UPDATE
odpovídá stejným řádkům ve stejném pořadí:
Co se stane pro tento dotaz je, že vlákna najdou řádek s col=2 a všechna se pokusí chytit zámek zápisu na tuto n-tici. Přesně jeden z nich uspěje. Ostatní zablokují čekání na potvrzení transakce prvního vlákna.
Tento první tx zapíše, odevzdá a vrátí počet řádků 1. Potvrzení uvolní zámek.
Ostatní vysílače se znovu pokusí chytit zámek. Jeden po druhém uspějí. Každá transakce následně projde následujícím procesem:
- Získejte zámek zápisu na spornou n-tici.
- Znovu zkontrolujte
WHERE col=2
stavu po získání zámku. - Opětovná kontrola ukáže, že podmínka již neodpovídá, takže
UPDATE
tento řádek přeskočí. - Položka
UPDATE
nemá žádné další řádky, takže nebude hlásit žádné aktualizované řádky. - Zavázat se, uvolnit zámek pro další vysílací stanici, která se jej pokusí získat.
V tomto jednoduchém případě zamykání na úrovni řádků a opakovaná kontrola stavu efektivně serializuje aktualizace. Ve složitějších případech ani moc ne.
Můžete to snadno demonstrovat. Otevřete řekněme čtyři relace psql. V první uzamkněte tabulku pomocí BEGIN; LOCK TABLE test;
. Ve zbývajících relacích spusťte identické UPDATE
s - zablokují zámek na úrovni stolu. Nyní uvolněte zámek pomocí COMMIT
na své první sezení. Sledujte, jak závodí. Pouze jeden bude hlásit počet řádků 1, ostatní 0. To lze snadno automatizovat a naskriptovat pro opakování a škálování na více připojení/vlákna.
Chcete-li se dozvědět více, přečtěte si pravidla pro souběžné psaní , strana 11 z Problémy se souběžností PostgreSQL - a pak si přečtěte zbytek této prezentace.
A pokud sloupec1 není jedinečný?
Jak Kevin poznamenal v komentářích, pokud col
není jedinečný, takže se může shodovat více řádků a poté různá provedení UPDATE
mohli získat různé objednávky. To se může stát, pokud si vyberou různé plány (řekněme, že jeden je přes PREPARE
a EXECUTE
a další je přímý, nebo si zahráváte s enable_
GUC) nebo pokud plán, který všichni používají, používá nestabilní druh stejných hodnot. Pokud dostanou řádky v jiném pořadí, pak tx1 zamkne jednu n-tice, tx2 zamkne další a pak se každý pokusí získat zámky na již zamčené n-tice. PostgreSQL zruší jeden z nich s výjimkou uváznutí. To je další dobrý důvod, proč vše kód vaší databáze by měl vždy buďte připraveni zopakovat transakce.
Pokud jste opatrní, abyste zajistili souběžné UPDATE
s vždy získáte stejné řádky ve stejném pořadí, stále se můžete spolehnout na chování popsané v první části odpovědi.
Je frustrující, že PostgreSQL nenabízí UPDATE ... ORDER BY
takže zajistit, aby aktualizace vždy vybíraly stejné řádky ve stejném pořadí, není tak jednoduché, jak byste si možná přáli. A SELECT ... FOR UPDATE ... ORDER BY
následovaný samostatným UPDATE
je často nejbezpečnější.
Složitější dotazy, systémy řazení do front
Pokud provádíte dotazy s více fázemi, zahrnující více n-tic nebo podmínky jiné než rovnost, můžete získat překvapivé výsledky, které se liší od výsledků sériového provádění. Zejména souběžné běhy čehokoli jako:
UPDATE test SET col = 1 WHERE col = (SELECT t.col FROM test t ORDER BY t.col LIMIT 1);
nebo jiné snahy o vybudování jednoduchého „frontového“ systému bude *nebudou* fungovat tak, jak očekáváte. Viz Dokumenty PostgreSQL o souběžnosti a tato prezentace pro více informací.
Pokud chcete pracovní frontu podloženou databází, existují osvědčená řešení, která zvládnou všechny překvapivě komplikované rohové případy. Jedním z nejpopulárnějších je PgQ . Existuje užitečný papír PgCon na dané téma a vyhledání Google pro „postgresql queue“ je plná užitečných výsledků.
BTW, namísto LOCK TABLE
můžete použít SELECT 1 FROM test WHERE col = 2 FOR UPDATE;
získat zámek zápisu právě na n-tice. To zablokuje aktualizace proti němu, ale neblokuje zápisy do jiných n-tic ani blokuje jakékoli čtení. To vám umožní simulovat různé druhy problémů souběžnosti.