Fra Transaktionsisolering side:
En EXPLAIN
på den SELECT
kan fortælle dig, hvad forespørgselsplanen tages, men hvis tabellen er lille (eller tom!), vil PostgreSQL næsten helt sikkert vælge en sekventiel scanning i stedet for at referere til indekset. Dette vil forårsage en prædikatlås på hele bordet, hvilket forårsager serialiseringsfejl, når en anden transaktion gør noget ved bordet.
På mit system:
isolation=# EXPLAIN SELECT * from mydevice where cid = 1;
QUERY PLAN
----------------------------------------------------------
Seq Scan on mydevice (cost=0.00..23.38 rows=5 width=46)
Filter: (cid = 1)
(2 rows)
Du kan prøve at tilføje et indeks og tvinge det til at bruge det:
isolation=# CREATE INDEX mydevice_cid_key ON mydevice (cid);
CREATE INDEX
isolation=# SET enable_seqscan = off;
SET
isolation=# EXPLAIN SELECT * from mydevice where cid = 1;
QUERY PLAN
----------------------------------------------------------------------------------
Index Scan using mydevice_cid_key on mydevice (cost=0.00..8.27 rows=1 width=46)
Index Cond: (cid = 1)
(2 rows)
Dette er dog ikke den rigtige løsning. Lad os bakke lidt op.
Serializable er beregnet til at garantere, at transaktioner vil have nøjagtig samme effekt, som hvis de blev kørt efter hinanden, på trods af at du faktisk kører disse transaktioner samtidigt. PostgreSQL har ikke uendelige ressourcer, så selvom det er rigtigt, at det sætter prædikatlåse på data, som din forespørgsel faktisk får adgang til, kan "data" betyde mere end "returnerede rækker".
PostgreSQL vælger at markere serialiseringsfejl, når den mener, der kan være et problem, ikke når det er sikkert. (Derfor, hvordan det generaliserer rækkelåse til sidelåse.) Dette designvalg forårsager falske positiver, såsom den i dit eksempel. Falske positiver er mindre end ideelle, men det påvirker ikke korrektheden af isolationssemantikken.
Fejlmeddelelsen er:
ERROR: could not serialize access due to read/write dependencies among transactions
DETAIL: Reason code: Canceled on identification as a pivot, during commit attempt.
HINT: The transaction might succeed if retried.
Det hint er nøglen. Din applikation skal fange serialiseringsfejl og prøve hele handlingen igen . Dette gælder hver gang SERIALIZABLE
er i spil -- det garanterer seriel korrekthed på trods af samtidighed, men det kan ikke gøre det uden hjælp fra din applikation. Sagt på en anden måde, hvis du rent faktisk laver samtidige ændringer, er den eneste måde PostgreSQL kan opfylde isolationskravene på at bede din applikation om at serialisere sig selv. Således: