Dina angivna garantier gäller i detta enkla fall, men inte nödvändigtvis i lite mer komplexa frågor. Se slutet av svaret för exempel.
Det enkla fallet
Förutsatt att col1 är unik, har exakt ett värde "2" eller har stabil ordning så varje UPDATE
matchar samma rader i samma ordning:
Vad som kommer att hända för den här frågan är att trådarna kommer att hitta raden med col=2 och alla försöker få tag i ett skrivlås på den tupeln. Exakt en av dem kommer att lyckas. De andra kommer att blockera i väntan på att den första trådens transaktion ska begås.
Den första tx kommer att skriva, commit och returnera ett radantal på 1. Bekräftelsen släpper låset.
De andra tx:erna kommer igen att försöka ta tag i låset. En efter en kommer de att lyckas. Varje transaktion kommer i sin tur att gå igenom följande process:
- Få skrivlåset på den omtvistade tuppeln.
- Kontrollera
WHERE col=2
igen skick efter att ha fått låset. - Omkontrollen visar att villkoret inte längre matchar så
UPDATE
hoppar över den raden. UPPDATERING har inga andra rader så det kommer att rapportera noll rader uppdaterade. - Bekräfta, släpp låset för nästa sändning som du försöker få tag i det.
I detta enkla fall serialiserar radnivålåsningen och tillståndskontrollen effektivt uppdateringarna. I mer komplexa fall, inte så mycket.
Du kan enkelt demonstrera detta. Öppna säg fyra psql-sessioner. I den första låser du tabellen med BEGIN; LOCK TABLE test;
. I resten av sessionerna kör identisk UPDATE
s - de kommer att blockera på bordsnivålåset. Släpp nu låset med COMMIT
din första session. Se dem tävla. Endast en kommer att rapportera ett radantal på 1, de andra kommer att rapportera 0. Detta är lätt automatiserat och skriptas för upprepning och skala upp till fler anslutningar/trådar.
Om du vill veta mer, läs regler för samtidig skrivning , sida 11 i Kontaktproblem med PostgreSQL - och läs sedan resten av presentationen.
Och om kol1 är icke-unik?
Som Kevin noterade i kommentarerna, om col
är inte unik så du kan matcha flera rader, sedan olika exekveringar av UPDATE
kunde få olika beställningar. Detta kan hända om de väljer olika planer (säg att en är en via en PREPARE
och EXECUTE
och en annan är direkt, eller så bråkar du med enable_
GUC) eller om planen de alla använder använder en instabil sorts lika värden. Om de får raderna i en annan ordning kommer tx1 att låsa en tuppel, tx2 kommer att låsa en annan, sedan kommer de att försöka få lås på varandras redan låsta tuplar. PostgreSQL kommer att avbryta en av dem med ett dödläge undantag. Detta är ännu en bra anledning till varför alla din databaskod ska alltid var beredd att försöka transaktioner igen.
Om du är noga med att se till att UPPDATERA
samtidigt Om du alltid får samma rader i samma ordning kan du fortfarande lita på beteendet som beskrivs i den första delen av svaret.
Frustrerande nog erbjuder PostgreSQL inte UPPDATERA ... BESTÄLL EFTER
så att se till att dina uppdateringar alltid väljer samma rader i samma ordning är inte så enkelt som du kanske önskar. A VÄLJ ... FÖR UPPDATERING ... BESTÄLL EFTER
följt av en separat UPPDATERING
är ofta säkrast.
Mer komplexa frågor, kösystem
Om du gör frågor med flera faser, involverar flera tupler eller andra förhållanden än jämlikhet kan du få överraskande resultat som skiljer sig från resultaten av en seriell körning. I synnerhet samtidiga körningar av något liknande:
UPDATE test SET col = 1 WHERE col = (SELECT t.col FROM test t ORDER BY t.col LIMIT 1);
eller andra försök att bygga ett enkelt "kösystem" kommer *misslyckas* med att fungera som du förväntar dig. Se PostgreSQL-dokument om samtidighet och denna presentation för mer information.
Om du vill ha en arbetskö uppbackad av en databas finns det väl beprövade lösningar som hanterar alla förvånansvärt komplicerade hörnfall. En av de mest populära är PgQ . Det finns ett användbart PgCon-dokument om ämnet och en Google-sökning efter 'postgresql queue' är full av användbara resultat.
BTW, istället för en LOCK TABLE
du kan använda SELECT 1 FROM test WHERE col =2 FÖR UPPDATERING;
för att få skrivlås på just det på tuple. Det kommer att blockera uppdateringar mot det men inte blockera skrivningar till andra tupler eller blockera läsningar. Det gör att du kan simulera olika typer av samtidighetsproblem.