sql >> Database >  >> RDS >> PostgreSQL

Kunnen meerdere threads dubbele updates veroorzaken op een beperkte set?

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.




  1. PRIMARY KEY-definitie in MySQL CREATE TABLE-instructie

  2. Is er een standaardaanpak voor het omgaan met ongeordende arrays (sets) in PostgreSQL?

  3. Hoe de Exp()-functie werkt in PostgreSQL

  4. MySQL-ronde datum naar het begin van de week en maand