Dine angivne garantier gælder i dette simple tilfælde, men ikke nødvendigvis i lidt mere komplekse forespørgsler. Se slutningen af svaret for eksempler.
Den simple sag
Forudsat at col1 er unik, har præcis én værdi "2" eller har stabil rækkefølge, så hver OPDATERING
matcher de samme rækker i samme rækkefølge:
Hvad der vil ske for denne forespørgsel er, at trådene vil finde rækken med col=2 og alle forsøger at få fat i en skrivelås på den tuple. Præcis én af dem vil lykkes. De andre vil blokere og vente på, at den første tråds transaktion begås.
Den første tx vil skrive, commit og returnere et rækkeantal på 1. Commit vil frigive låsen.
De andre tx'er vil igen forsøge at få fat i låsen. En efter en vil de lykkes. Hver transaktion vil igen gennemgå følgende proces:
- Få skrivelåsen på den omtvistede tuple.
- Tjek igen
WHERE col=2
tilstand efter at have fået låsen. - Fornyet kontrol vil vise, at betingelsen ikke længere stemmer overens, så
OPDATERING
springer den række over. OPDATERING har ingen andre rækker, så den vil rapportere nul rækker opdateret. - Bekræft, frigør låsen til næste tx, der prøver at få fat i den.
I dette simple tilfælde serialiserer rækkeniveaulåsningen og tilstandsgenkontrollen effektivt opdateringerne. I mere komplekse tilfælde, ikke så meget.
Det kan du nemt demonstrere. Åbn sige fire psql-sessioner. I den første låser du tabellen med BEGIN; LOCK TABLE test;
. I resten af sessionerne køres identisk OPDATERING
s - de vil blokere på bordniveaulåsen. Slip nu låsen med COMMIT
din første session. Se dem race. Kun én vil rapportere et rækkeantal på 1, de andre vil rapportere 0. Dette er nemt automatiseret og scriptet til gentagelse og opskalering til flere forbindelser/tråde.
For at lære mere, læs regler for samtidig skrivning , side 11 i Konkurrenceproblemer med PostgreSQL - og læs så resten af den præsentation.
Og hvis col1 er ikke-unik?
Som Kevin bemærkede i kommentarerne, hvis col
er ikke unik, så du kan matche flere rækker, derefter forskellige udførelser af OPDATERING
kunne få forskellige bestillinger. Dette kan ske, hvis de vælger forskellige planer (f.eks. er en via en PREPARE
). og EXECUTE
og en anden er direkte, eller du roder med enable_
GUC'er), eller hvis planen, de alle bruger, bruger en ustabil slags lige værdier. Hvis de får rækkerne i en anden rækkefølge, vil tx1 låse en tuple, tx2 vil låse en anden, så vil de hver især forsøge at få låse på hinandens allerede låste tuples. PostgreSQL vil afbryde en af dem med en deadlock undtagelse. Dette er endnu en god grund til at alle din databasekode skal altid være parat til at prøve transaktioner igen.
Hvis du er omhyggelig med at sikre samtidig OPDATERING
s altid får de samme rækker i samme rækkefølge, kan du stadig stole på den adfærd, der er beskrevet i den første del af svaret.
Frustrerende nok tilbyder PostgreSQL ikke OPDATERING ... BESTIL EFTER
så det er ikke så enkelt, som du måske ønsker at sikre, at dine opdateringer altid vælger de samme rækker i samme rækkefølge. A VÆLG ... FOR OPDATERING ... BESTIL EFTER
efterfulgt af en separat OPDATERING
er ofte sikrest.
Mere komplekse forespørgsler, køsystemer
Hvis du laver forespørgsler med flere faser, involverer flere tuples eller andre forhold end lighed, kan du få overraskende resultater, der adskiller sig fra resultaterne af en seriel udførelse. Især samtidige kørsler af noget som:
UPDATE test SET col = 1 WHERE col = (SELECT t.col FROM test t ORDER BY t.col LIMIT 1);
eller andre bestræbelser på at bygge et simpelt "kø"-system vil *ikke* fungerer som du forventer. Se PostgreSQL-dokumenterne om samtidighed og denne præsentation for mere info.
Hvis du ønsker en arbejdskø understøttet af en database, er der gennemtestede løsninger, der håndterer alle de overraskende komplicerede hjørnesager. En af de mest populære er PgQ . Der er et nyttigt PgCon-papir om emnet, og en Google-søgning efter 'postgresql-kø' er fuld af nyttige resultater.
BTW, i stedet for en LOCK TABLE
du kan bruge SELECT 1 FROM test WHERE col =2 FOR UPDATE;
at opnå skrivelås på netop det på tuple. Det vil blokere opdateringer mod det, men ikke blokere skrivninger til andre tuples eller blokere nogen læsninger. Det giver dig mulighed for at simulere forskellige former for samtidighedsproblemer.