sql >> Databáze >  >> RDS >> PostgreSQL

Může více vláken způsobit duplicitní aktualizace na omezené sadě?

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.




  1. MySQL:odstraňte po sobě jdoucí duplicitní hodnoty

  2. Co jsou data a informace a objemy dat

  3. Odstraňte duplikáty na základě dvou sloupců a ponechte řádek, který má minimální hodnotu jiného sloupce

  4. Připojte se k SQLite