Uw vermelde garanties zijn van toepassing in dit eenvoudige geval, maar niet noodzakelijkerwijs in iets complexere vragen. Zie het einde van het antwoord voor voorbeelden.
Het eenvoudige geval
Ervan uitgaande dat col1 uniek is, precies één waarde "2" heeft, of een stabiele volgorde heeft, zodat elke UPDATE
komt overeen met dezelfde rijen in dezelfde volgorde:
Wat er voor deze query zal gebeuren, is dat de threads de rij met col=2 zullen vinden en allemaal proberen een schrijfblokkering op die tuple te krijgen. Precies één van hen zal slagen. De anderen zullen het wachten op de transactie van de eerste thread blokkeren.
Die eerste tx schrijft, commit en retourneert een rijtelling van 1. De commit zal de vergrendeling opheffen.
De andere tx'en zullen opnieuw proberen het slot te grijpen. Eén voor één zullen ze slagen. Elke transactie doorloopt op zijn beurt het volgende proces:
- Verkrijg het schrijfslot op de betwiste tuple.
- Controleer opnieuw de
WHERE col=2
staat na het verkrijgen van het slot. - De nieuwe controle zal aantonen dat de voorwaarde niet meer overeenkomt, dus de
UPDATE
slaat die rij over. - De
UPDATE
heeft geen andere rijen, dus het rapporteert nul bijgewerkte rijen. - Toeleggen, het slot vrijgeven voor de volgende tx die probeert het te pakken te krijgen.
In dit eenvoudige geval zorgt de vergrendeling op rijniveau en de hercontrole van de voorwaarden ervoor dat de updates effectief worden geserialiseerd. In meer complexe gevallen niet zo veel.
Dit kunt u eenvoudig aantonen. Open zeg maar vier psql-sessies. In de eerste, vergrendel de tabel met BEGIN; LOCK TABLE test;
. In de rest van de sessies lopen identieke UPDATE
s - ze zullen blokkeren op de tafelniveauvergrendeling. Ontgrendel nu het slot met COMMIT
uw eerste sessie. Kijk hoe ze racen. Slechts één rapporteert een rijtelling van 1, de anderen rapporteren 0. Dit is eenvoudig te automatiseren en scripts voor herhaling en opschalen naar meer verbindingen/threads.
Lees voor meer informatie regels voor gelijktijdig schrijven , pagina 11 van Concurrency-problemen met PostgreSQL - en lees dan de rest van die presentatie.
En als col1 niet uniek is?
Zoals Kevin opmerkte in de opmerkingen, als col
is niet uniek, dus u kunt overeenkomen met meerdere rijen en vervolgens met verschillende uitvoeringen van de UPDATE
verschillende bestellingen kunnen krijgen. Dit kan gebeuren als ze verschillende plannen kiezen (bijvoorbeeld een via a PREPARE
en EXECUTE
en een andere is direct, of je knoeit met de enable_
GUC's) of als het plan dat ze allemaal gebruiken een onstabiel soort gelijke waarden gebruikt. Als ze de rijen in een andere volgorde krijgen, zal tx1 één tuple vergrendelen, tx2 zal een andere vergrendelen, dan zullen ze elk proberen om vergrendelingen te krijgen op elkaars reeds vergrendelde tuples. PostgreSQL zal een van hen afbreken met een deadlock-uitzondering. Dit is nog een goede reden waarom alle uw databasecode moet altijd wees voorbereid om transacties opnieuw te proberen.
Als u ervoor zorgt dat gelijktijdige UPDATE
Als u altijd dezelfde rijen in dezelfde volgorde krijgt, kunt u nog steeds vertrouwen op het gedrag dat in het eerste deel van het antwoord wordt beschreven.
Frustrerend genoeg biedt PostgreSQL geen UPDATE ... ORDER BY
dus ervoor zorgen dat uw updates altijd dezelfde rijen in dezelfde volgorde selecteren, is niet zo eenvoudig als u zou willen. EEN SELECT ... FOR UPDATE ... ORDER BY
gevolgd door een aparte UPDATE
is vaak het veiligst.
Complexere zoekopdrachten, wachtrijsystemen
Als u query's uitvoert met meerdere fasen, met meerdere tuples of andere voorwaarden dan gelijkheid, kunt u verrassende resultaten krijgen die verschillen van de resultaten van een seriële uitvoering. In het bijzonder gelijktijdige runs van iets als:
UPDATE test SET col = 1 WHERE col = (SELECT t.col FROM test t ORDER BY t.col LIMIT 1);
of andere pogingen om een eenvoudig "wachtrij"-systeem te bouwen zal * faalt * om te werken zoals u verwacht. Zie de PostgreSQL-documenten over gelijktijdigheid en deze presentatie voor meer info.
Als u een werkwachtrij wilt hebben die wordt ondersteund door een database, zijn er goed geteste oplossingen die alle verrassend gecompliceerde hoekgevallen aankunnen. Een van de meest populaire is PgQ . Er is een handig PgCon-papier over het onderwerp, en een Google-zoekopdracht naar 'postgresql queue' staat vol met nuttige resultaten.
BTW, in plaats van een LOCK TABLE
je kunt SELECT 1 FROM test WHERE col = 2 FOR UPDATE;
om een schrijfblokkering op alleen dat op tuple te verkrijgen. Dat blokkeert updates ertegen, maar blokkeert geen schrijfacties naar andere tuples of blokkeert leesbewerkingen. Hiermee kunt u verschillende soorten gelijktijdigheidsproblemen simuleren.