Da Isolamento della transazione pagina:
Un EXPLAIN
su quel SELECT
può dirti quale piano di query viene preso, ma se la tabella è piccola (o vuota!), PostgreSQL quasi sicuramente sceglierà una scansione sequenziale invece di fare riferimento all'indice. Ciò causerà un blocco del predicato sull'intera tabella, causando un errore di serializzazione ogni volta che un'altra transazione esegue azioni sulla tabella.
Sul mio sistema:
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)
Potresti provare ad aggiungere un indice e forzarlo a usarlo:
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)
Tuttavia, questa non è la soluzione corretta. Facciamo un po' di backup.
Serializzabile ha lo scopo di garantire che le transazioni avranno esattamente lo stesso effetto come se fossero eseguite una dopo l'altra, nonostante il fatto che tu stia effettivamente eseguendo queste transazioni contemporaneamente. PostgreSQL non ha risorse infinite, quindi, sebbene sia vero che metta i blocchi dei predicati sui dati a cui la tua query accede effettivamente, "dati" può significare più di "righe restituite".
PostgreSQL sceglie di contrassegnare gli errori di serializzazione quando pensa che potrebbe esserci un problema, non quando è certo. (Da qui il modo in cui generalizza i blocchi di riga ai blocchi di pagina.) Questa scelta di progettazione causa falsi positivi, come quello nell'esempio. I falsi positivi sono tutt'altro che ideali, tuttavia, non influiscono sulla correttezza della semantica di isolamento.
Il messaggio di errore è:
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.
Quel suggerimento è fondamentale. La tua applicazione deve rilevare gli errori di serializzazione e ritentare l'intera operazione . Questo è vero ogni volta che SERIALIZABLE
è in gioco -- garantisce la correttezza seriale nonostante la concorrenza, ma non può farlo senza l'aiuto della tua applicazione. In altre parole, se stai effettivamente eseguendo modifiche simultanee, l'unico modo in cui PostgreSQL può soddisfare i requisiti di isolamento è chiedere alla tua applicazione di serializzare se stessa. Quindi: