Documentazione di PostgreSQL 9.0 > Il linguaggio SQL > Controllo concorrente > Lock esplicito
PrecedenteIsolamento transazionaleControlli di consistenza dei dati a livello di applicazioneSuccessivo

13.3. Lock esplicito

PostgreSQL™ fornisce varie modalità di lock per controllare accessi concorrenti ai dati nelle tabelle. Queste modalità possono essere usate per fare dei lock controllate dall'applicazione in situazioni dove MVCC non fornisce ilcomportamento desiderato. Inoltre, la maggior parte dei comandi PostgreSQL™ acquisiscono automaticamente lock delle modalità appropriate per assicurare che tabelle a cui si fa riferimento non siano cancellate o modificate in modi incompatibili mentre il comando è in esecuzione. (Per esempio, TRUNCATE non può essere eseguito concorrentemente ad altre operazioni sulla stessa tabella in maniera sicura, e così ottiene un lock esclusivo sulla tabella per assicurarlo).

Per esaminare un elenco dei lock attualmente presenti in un server database, usare la vista di sistema pg_locks. Per maggiori informazioni sul monitoraggio dello stato del sottosistema di gestione dei lock, si veda Capitolo 27, Monitoring Database Activity.

13.3.1. Table-Level Locks

L'elenco sottostante mostra le modalità di lock disponibili a i contesti in cui sono usati automaticamente da PostgreSQL™. È anche possibile ottenere uno qualsiasi di questi lock esplicitamente col comando LOCK(7). Ricordare che tutte queste modalità di lock sono lock a livello di tabella, anche se il nome contiene la parola «row»; i nomi delle modalità di lock sono storici. In qualche modo i nomi rispecchiano l'utilizzo tipico di ogni modalità di lock - ma la semantica è sempre la stessa. L'unica vera differenza tra una modalità di lock e un'altra è l'insieme di modalità di lock con le quali ognuna va in conflitto (si veda Tabella 13.2, «Modalità di lock che vanno in conflitto»). Due transazioni non possono ottenere lock di modalità che vanno in conflitto sulla stessa tabella nello stesso momento. (Comunque, una transazione non va mai in conflitto con sè stessa. Per esempio, potrebbe ottenere un lock ACCESS EXCLUSIVE e più tardi un lock ACCESS SHARE sulla stessa tabella). Modalità di lock che non vanno in conflitto possono essere trattenute concorrentemente da molte transazioni. Notare in particolare che alcune modalità di lock vanno in conflitto con sè stesse (per esempio, un lock ACCESS EXCLUSIVE non può essere trattenuto da più di una transazione alla volta) mentre gli altri non vanno in conflitto con sè stessi (per esempio, un lock ACCESS SHARE può essere trattenuto da molteplici transazioni).

Modalità di lock a livello di tabella

ACCESS SHARE

Va in conflitto solamente con la modalità di lock ACCESS EXCLUSIVE.

Il comando SELECT acquisisce un lock di questo tipo sulle tabelle referenziate. In generale, qualsiasi query che legge una tabella e non la modifica acquisirà questo tipo di lock.

ROW SHARE

Va in conflitto con le modalità di lock EXCLUSIVE e ACCESS EXCLUSIVE.

I comandi SELECT FOR UPDATE e SELECT FOR SHARE acquisiscono un lock di questo tipo sulla tabella/e richieste (in aggiunta a dei lock ACCESS SHARE su qualsiasi altra tabella che è referenziata ma non selezionata FOR UPDATE/FOR SHARE).

ROW EXCLUSIVE

VA in conflitto con le modalità SHARE, SHARE ROW EXCLUSIVE, EXCLUSIVE e ACCESS EXCLUSIVE.

I comandi UPDATE, DELETE, e INSERT acquisiscono questa modalità di lock sulla tabella desiderata (in aggiunta a dei lock ACCESS SHARE su qualsiasi altra tabella referenziata). In generale, questa modalità di lock sarà acquisita da qualsiasi comando che modifica i dati in una tabella.

SHARE UPDATE EXCLUSIVE

VA in conflitto con le modalità di lock SHARE UPDATE EXCLUSIVE, SHARE, SHARE ROW EXCLUSIVE, EXCLUSIVE e ACCESS EXCLUSIVE. Questa modalità protegge una tabella rispetto a cambiamenti di schema concorrenti e al VACUUM.

Acquisito da VACUUM (senza FULL), ANALYZE e CREATE INDEX CONCURRENTLY.

SHARE

Va in conflitto con le modalità ROW EXCLUSIVE, SHARE UPDATE EXCLUSIVE, SHARE ROW EXCLUSIVE, EXCLUSIVE e ACCESS EXCLUSIVE. Questa modalità protegge una tabella rispetto a cambiamenti di dato concorrente.

Acquisito da CREATE INDEX (senza CONCURRENTLY).

SHARE ROW EXCLUSIVE

Va in conflitto con le modalità ROW EXCLUSIVE, SHARE UPDATE EXCLUSIVE, SHARE, SHARE ROW EXCLUSIVE, EXCLUSIVE e ACCESS EXCLUSIVE.

Acquisito da CREATE TRIGGER, CREATE RULE (ad eccezione di regole ON SELECT) ed in alcuni casi ALTER TABLE.

EXCLUSIVE

Va in conflitto con le modalità ROW SHARE, ROW EXCLUSIVE, SHARE UPDATE EXCLUSIVE, SHARE, SHARE ROW EXCLUSIVE, EXCLUSIVE e ACCESS EXCLUSIVE. Questa modalità permette solo lock ACCESS SHARE concorrenti, per es., con una transazione che possiede questo tipo di lock possono procedere in parallelo solo le letture dalla tabella.

Questa modalità di lock non viene acquisita automaticamente su tabelle utente da nessun comando PostgreSQL™. Comunque viene acquisita su certi cataloghi di sistema in alcune operazioni.

ACCESS EXCLUSIVE

Va in conflitto con lock di tutti i tipi (ACCESS SHARE, ROW SHARE, ROW EXCLUSIVE, SHARE UPDATE EXCLUSIVE, SHARE, SHARE ROW EXCLUSIVE, EXCLUSIVE e ACCESS EXCLUSIVE). Questa modalità garantisce che chi possiede il lock sia l'unica transazione che ha accesso alla tabella in qualsiasi modo.

Acquisito dai comandi DROP TABLE, TRUNCATE, REINDEX, CLUSTER e VACUUM FULL, così come la maggior parte delle varianti di ALTER TABLE. Questa, inoltre, è la moda predefinita per le istruzioni LOCK TABLE che non specificano una modalità esplicitamente.

[Suggerimento]

Suggerimento

Solamente un lock ACCESS EXCLUSIVE blocca un'istruzione SELECT (senza FOR UPDATE/SHARE)

Once acquired, a lock is normally held till end of transaction. But if a lock is acquired after establishing a savepoint, the lock is released immediately if the savepoint is rolled back to. This is consistent with the principle that ROLLBACK cancels all effects of the commands since the savepoint. The same holds for locks acquired within a PL/pgSQL exception block: an error escape from the block releases locks acquired within it.

Tabella 13.2. Modalità di lock che vanno in conflitto

Modalità di lock richiestaModalità di lock corrente
ACCESS SHAREROW SHAREROW EXCLUSIVESHARE UPDATE EXCLUSIVESHARESHARE ROW EXCLUSIVEEXCLUSIVEACCESS EXCLUSIVE
ACCESS SHARE       X
ROW SHARE      XX
ROW EXCLUSIVE    XXXX
SHARE UPDATE EXCLUSIVE   XXXXX
SHARE  XX XXX
SHARE ROW EXCLUSIVE  XXXXXX
EXCLUSIVE XXXXXXX
ACCESS EXCLUSIVEXXXXXXXX

13.3.2. Lock a livello di riga

In aggiunta ai lock a livello di tabella, ci sono lock a livello di riga, che possono essere lock esclusivi o condivisi. Un lock esclusivo su una specifica riga viene automaticamente acquisito quando la riga viene aggiornata o cancellata. Il lock è conservato fino a che la transazione fa un commit o un rollback, esattamente come lock a livello di tabella. Lock a livello di riga non hanno effetto sull'interrogazione dei dati; bloccano solo scritture sulla stessa riga.

Per ottenere un lock di riga esclusivo su una riga senza modificare effettivamente la riga, selezionare la riga con SELECT FOR UPDATE. Notare che una volta che il lock di riga viene acquisito, la transazione può aggiornare la riga molte volte senza temere conflitti.

Per acquisire un lock di riga condiviso, selezionare la riga con SELECT FOR SHARE. Un lock condiviso non previene il fatto che altre transazioni possano acquisire lo stesso lock condiviso. Comunque, nessuna dtransazione ha il permesso di aggiornare, cancellare, o ottenere un lock esclusivo su una riga sulla quale qualsiasi altra transazione mantiene un lock condiviso. Qualsiasi tentativo di farlo si bloccherà finchè il lock non viene rilasciato.

PostgreSQL™ non mantiene nessuna informazione sulle righe modificate in memoria, così non c'è un limite per il numero di righe con lock contemporanee. Comunque, fare il lock di una riga potrebbe causare una scrittura su disco, per es., SELECT FOR UPDATE modifica le righe selezionate per contrassegnarle come sottoposte a lock, e quindi risulterà in scritture su disco.

In aggiunta a lock di tabella e riga, vengono usati locki condivisi/esclusivi a livello di pagina per controllare gli accessi in lettura/scrittura alle pagine della tabella nel buffer condiviso. Questi lock sono rilasciati immediatamente dopo che una riga viene "fetched" o aggiornata. Gli sviluppatori di applicazioni normalmente non devono preoccuparsi dei lock di pagina, ma sono menzionati qui per completezza.

13.3.3. Deadlock

L'uso di lock espliciti può incrementare la probabilità di deadlock, in cui due (o più) transazioni mantengono entrambe lock che gli altri vogliono. Per esempio, se la transazione uno acquisisce un lock esclusivo sulla tabella A e successivamente prova ad acquisire un lock esclusivo sulla tabella B, mentre la transazione 2 ha già eseguito un lock esclusivo sulla tabella B e adesso vuole un lock esclusivo sulla tabella A, allora nessuna delle due può procedere. PostgreSQL™ riconosce automaticamente situazioni di deadlock e le risolve annullando una delle transazioni coinvolte, permettendo all'altra/e di completarsi. (È difficile predirre esattamente quale transazione sarà annullata e quindi non si dovrebbe farci affidamento).

Notare che i deadlock possono avvenire anche come risultato di lock di riga (e così, possono accadere anche se non viene usato un lock esplicito). Considerare il caso in cui due transazioni concorrenti modificano una tabella. La prima transazione esegue:

UPDATE accounts SET balance = balance + 100.00 WHERE acctnum = 11111;

Questo acquisisce un lock di riga sulla riga con il numero di account specificato. Quindi, la seconda transazione esegue:

UPDATE accounts SET balance = balance + 100.00 WHERE acctnum = 22222;
UPDATE accounts SET balance = balance - 100.00 WHERE acctnum = 11111;

La prima istruzione UPDATE ottiene con successo un lock di riga sulla riga specificata, e così riesce ad aggiornare con successo quella riga. Comunque, la seconda istruzione UPDATE scopre che la riga che sta tentando di aggiornare è già stata sottoposta a lock, e quindi aspetta che la transazione che aveva acquisito il lock sia completa. La transazione due aspetta quindi che la transazione uno dia completata prima di continuare l'esecuzione. Ora, la transazione uno esegue:

UPDATE accounts SET balance = balance - 100.00 WHERE acctnum = 22222;

La transazione uno tenta di ottenere un lock di riga sulla riga specificata, ma non può: la transazione due possiede già quel lock. Così aspetta che la transazione due sia completa. Così, la transazione uno è bloccata sulla transazione due, e la transazione due è bloccata sulla transazione uno: una condizione di deadlock. PostgreSQL™ si accorge di questa situazione e annulla una delle transazioni.

La miglior difesa contro i deadlock, generalmente, è evitare che diventi certo che tutte le applicazioni che usano un database acquisiscano lock su molteplici oggetti in un ordine costante. Nell'esempio sopra, se entrambe le transazioni hanno aggiornato le righe nello stesso ordine, non si saranno verificati deadlock. Si dovrebbe esser sicuri che il primo lock acquisito su un oggetto in una transazione è nella modalità più restrittiva che sarà necessaria a quell'oggetto. Se non è fattibile verificarlo in anticipo, allora i deadlock possono essere gestiti al volo ritentando le transazioni annullate a causa di deadlock.

Finchè nessuna situazione di deadlock viene riscontrata, una transazione che cerca o un lock di tabella o un lock di riga aspetterà indefinitamente che i lock in conflitto siano rilasciati. Questo significa che è una cattiva idea per le applicazioni trattenere transazioni aperte per lunghi periodi di tempo (per es., mentre aspettano input dell'utente).

13.3.4. Lock consultivi

PostgreSQL™ fornisce dei mezzi per la creazione di lock che hanno significati definiti dall'applicazione. Questi vengono chiamati lock consultivi, dato che il sistema non impone il loro urtilizzo - è compito dell'applicazione usarli correttamente. I lock consultivi possono essere utili per strategie di locking che sono poco maneggevoli per il modello MVCC. Una volta acquisito, un lock consultivo viene tenuto fino a che non viene rilasciato esplicitamente o se la sessione termina. A differenza dei lock standard, i lock consultivi non rispettano la semantica transazionale: un lock acquisito durante una transazione che successivamente è sottoposto a rollback sarà ancora trattenuto seguendo il rollback, e anche un unlock entra in vigore anche se le la transazione chiamante successivamente fallisce. Lo stesso lock può essere acquisito molteplici volte dal processo a cui appartiene: per ogni richiesta di lock ci deve essere una corrispondente richiesta di unlock prima che venga effettivamente rilasciato. (Se una sessione possiede già un dato lock, avranno luogo richieste aggiuntive, anche se altre sessioni sono in attesa del lock). Come tutti i lock in PostgreSQL™, un elenco completo dei lock consultivi legati a una qualsiasi sessione possono essere trovati nella vista di sistema pg_locks

I lock consultivi vengono allocati fuori dal pool di memoria condivisa di cui la dimensione viene definita dalle variabili di configurazione max_locks_per_transaction e max_connections. Si deve fare attenzione a non esaurire questa memoria o il server sarà incapace di concedere del tutto qualsiasi lock. Questo impone un limite superiore sul numero di lock consultivi concessi dal server, tipicamente in decine o centinaia di migliaia dipendentemente da come è configurato il server.

Un utilizzo comune dei lock consultivi è di emulare strategie di lock pessimistiche tipiche dei sistemi di gestione di dati chiamti «flat file». Mentre una flag immagazzinata in una tabella potrebbe essere usata per lo stesso scopo, i lock consultivi sono più veloci, evitando che MVCC si gonfi, e sono automaticamente puliti dal server alla fine della sessione. In certi casi, usare questo metodo di locking consultivo, specialmente in query che coinvolgono ordinamento esplicito e clausole LIMIT, si deve fare attenzione a controllare i lock acquisiti a causa dell'ordine in cui le espressioni SQL vengono valutate. Per esempio:

SELECT pg_advisory_lock(id) FROM foo WHERE id = 12345; -- ok
SELECT pg_advisory_lock(id) FROM foo WHERE id > 12345 LIMIT 100; -- pericoloso!
SELECT pg_advisory_lock(q.id) FROM
(
  SELECT id FROM foo WHERE id > 12345 LIMIT 100
) q; -- ok

Nelle query sopra, la seconda forma è pericolosa perchè non è garantito che la LIMIT venga applicata prima che la funzione di locking sia eseguita. Questo potrebbe causare che alcuni lock che l'applicazione non si aspettava siano acquisiti, per cui fallirebbe nel tentativo di rilasciarli (finchè la sessione non finisce). Dal punto di vista dell'applicazione, quei lock sarebbero pendenti, anche se ancora visibili in pg_locks.

Le funzioni poste per manipolare lock consultivi sono descritte in Tabella 9.61, «Funzioni di lock consultivi».

Documentazione di PostgreSQL 9.0 > Il linguaggio SQL > Controllo concorrente > Lock esplicito
PrecedenteIsolamento transazionaleControlli di consistenza dei dati a livello di applicazioneSuccessivo