Blocco di intervalli di chiavi
I blocchi di intervalli di chiavi consentono di proteggere un intervallo di righe incluse in modo implicito in un recordset letto da un'istruzione Transact-SQL quando si utilizza il livello di isolamento delle transazioni serializzabile. Con questo livello di isolamento, una query eseguita durante una transazione deve ottenere sempre lo stesso set di righe ogni volta che viene eseguita all'interno della stessa transazione. Il blocco di intervalli di chiavi garantisce che ciò avvenga impedendo ad altre transazioni di inserire nuove righe le cui chiavi rientrerebbero nell'intervallo di chiavi letto dalla transazione serializzabile.
Il blocco di intervalli di chiavi impedisce le letture fantasma, nonché gli inserimenti fantasma in un recordset a cui accede una transazione attraverso la protezione degli intervalli di chiavi tra le righe.
Il blocco di intervalli di chiavi viene applicato a un indice specificando i valori iniziale e finale delle chiavi. In questo modo si impedisce ogni tentativo di inserimento, aggiornamento o eliminazione di una riga con un valore di chiave che rientra nell'intervallo in quanto tali operazioni dovrebbero acquisire un blocco sull'indice. Una transazione serializzabile può ad esempio eseguire un'istruzione SELECT che legge tutte le righe i cui valori di chiave sono compresi tra 'AAA' e 'CZZ'. Un blocco di intervalli di chiavi applicato ai valori di chiave compresi tra 'AAA' e 'CZZ' impedisce ad altre transazioni di inserire righe con valori di chiave all'interno di tale intervallo, ad esempio 'ADG', 'BBD' o 'CAL'.
Modalità di blocco di intervalli di chiavi
I blocchi di intervalli di chiavi includono sia un componente intervallo sia un componente riga nel formato intervallo-riga:
Intervallo indica la modalità di blocco che protegge l'intervallo di righe compreso tra due voci di indice consecutive.
Riga indica la modalità di blocco che protegge la voce di indice.
Modalità indica la modalità di blocco combinato utilizzata. Le modalità di blocco di intervalli di chiavi sono composte da due parti: la prima parte rappresenta il tipo di blocco utilizzato per bloccare l'intervallo di indici (RangeT), mentre la seconda rappresenta il tipo di blocco utilizzato per bloccare una chiave specifica (K). Le due parti sono unite da un segno meno (-), ad esempio RangeT-K.
Intervallo
Riga
Modalità
Descrizione
RangeS
S
RangeS-S
Intervallo condiviso, blocco di risorsa condiviso; scansione intervallo serializzabile.
RangeS
U
RangeS-U
Intervallo condiviso, blocco di risorsa di aggiornamento; scansione aggiornamento serializzabile.
RangeI
Null
RangeI-N
Intervallo di inserimento, blocco di risorsa Null; utilizzato per verificare gli intervalli prima di inserire una nuova chiave nell'indice.
RangeX
X
RangeX-X
Intervallo esclusivo, blocco di risorsa esclusivo; utilizzato per aggiornare una chiave di un intervallo.
[!NOTA]
La modalità di blocco Null interna è compatibile con tutti gli altri tipi di blocco.
Le modalità di blocco di intervalli di chiavi sono basate sulla matrice di compatibilità illustrata di seguito che stabilisce quali blocchi sono compatibili con quelli ottenuti dalla sovrapposizione di chiavi e intervalli. Per informazioni sulla matrice completa di compatibilità dei blocchi, vedere Compatibilità tra blocchi.
|
Modalità concessa esistente |
|
|
|
|
|
|
---|---|---|---|---|---|---|---|
Modalità richiesta |
S |
U |
X |
RangeS-S |
RangeS-U |
RangeI-N |
RangeX-X |
Condiviso (S) |
Sì |
Sì |
No |
Sì |
Sì |
Sì |
No |
Aggiornamento (U) |
Sì |
No |
No |
Sì |
No |
Sì |
No |
Esclusivo (X) |
No |
No |
No |
No |
No |
Sì |
No |
RangeS-S |
Sì |
Sì |
No |
Sì |
Sì |
No |
No |
RangeS-U |
Sì |
No |
No |
Sì |
No |
No |
No |
RangeI-N |
Sì |
Sì |
Sì |
No |
No |
Sì |
No |
RangeX-X |
No |
No |
No |
No |
No |
No |
No |
Blocchi di conversione
I blocchi di conversione vengono creati quando un blocco di intervalli di chiavi è sovrapposto a un altro blocco.
Blocco 1 |
Blocco 2 |
Blocco di conversione |
---|---|---|
S |
RangeI-N |
RangeI-S |
U |
RangeI-N |
RangeI-U |
X |
RangeI-N |
RangeI-X |
RangeI-N |
RangeS-S |
RangeX-S |
RangeI-N |
RangeS-U |
RangeX-U |
I blocchi di conversione possono essere osservati per un breve periodo di tempo in diverse circostanze complesse, in alcuni casi durante l'esecuzione di processi simultanei.
Scansione intervallo serializzabile, recupero singleton, eliminazione e inserimento
Il blocco di intervalli di chiavi garantisce la serializzabilità quando si eseguono le operazioni seguenti:
Query di scansione intervallo
Recupero singleton di righe inesistenti
Operazioni di eliminazione
Operazioni di inserimento
Prima di eseguire un blocco di intervalli di chiavi, è necessario che siano soddisfatte le condizioni seguenti:
Il livello di isolamento della transazione deve essere impostato su SERIALIZABLE.
Query Processor deve utilizzare un indice per implementare il predicato di filtro dell'intervallo. La clausola WHERE in un'istruzione SELECT può ad esempio stabilire una condizione di intervallo con il predicato seguente: ColumnX BETWEEN N**'AAA'** AND N**'CZZ'**. Un blocco di intervalli di chiavi può essere acquisito solo se ColumnX è coperto da una chiave di indice.
Esempi
Gli esempi di blocco di intervalli di chiavi illustrati si basano sulla tabella e sull'indice seguenti.
Query di scansione intervallo
Per assicurare che una query per la scansione di intervalli sia serializzabile, è necessario che la query restituisca gli stessi risultati a ogni esecuzione all'interno della stessa transazione. Altre transazioni non devono inserire nuove righe nella query per la scansione di intervalli in quanto le righe potrebbero diventare inserimenti fantasma. Nella query seguente vengono ad esempio utilizzati la tabella e l'indice illustrati nella figura precedente:
SELECT name
FROM mytable
WHERE name BETWEEN 'A' AND 'C';
I blocchi di intervalli di chiavi vengono applicati alle voci di indice che corrispondono all'intervallo delle righe di dati in cui il nome è compreso tra i valori Adam e Dale, in modo da impedire l'inserimento o l'eliminazione di nuove righe. Sebbene il primo nome dell'intervallo sia Adam, il blocco di intervalli di chiavi in modalità RangeS-S applicato a questa voce di indice impedisce l'aggiunta prima del nome Adam di nuovi nomi che iniziano con la lettera A, ad esempio Abigail. In modo analogo, il blocco di intervalli di chiavi RangeS-S applicato alla voce di indice Dale impedisce l'aggiunta dopo il nome Carlos di nuovi nomi che iniziano con la lettera C, ad esempio Clive.
[!NOTA]
Il numero di blocchi RangeS-S mantenuti attivi è n+1, dove n è il numero di righe che soddisfano la query.
Recupero singleton di dati inesistenti
Se una query di una transazione tenta di selezionare una riga inesistente, la successiva esecuzione della query all'interno della stessa transazione deve restituire il medesimo risultato. A nessun'altra transazione è consentito inserire la riga inesistente. Si consideri ad esempio la query seguente:
SELECT name
FROM mytable
WHERE name = 'Bill';
Un blocco di intervalli di chiavi viene applicato alla voce di indice corrispondente all'intervallo di nomi compreso tra Ben e Bing in quanto il nome Bill verrebbe incluso alfabeticamente tra queste due voci di indice. Il blocco di intervalli di chiavi in modalità RangeS-S viene applicato alla voce di indice Bing, impedendo così ad altre transazioni di inserire valori, ad esempio Bill, tra le voci di indice Ben e Bing.
Operazioni di eliminazione
Quando viene eliminato un valore in una transazione, l'intervallo a cui appartiene tale valore non deve necessariamente rimanere bloccato per l'intera durata della transazione che esegue l'eliminazione. Per mantenere la serializzabilità è infatti sufficiente bloccare il valore della chiave eliminata fino al termine della transazione. Si consideri ad esempio l'istruzione DELETE seguente:
DELETE mytable
WHERE name = 'Bob';
Alla voce di indice corrispondente al nome Bob viene applicato un blocco esclusivo (X). Altre transazioni possono inserire o eliminare valori prima o dopo il valore eliminato Bob. I tentativi di lettura, inserimento o eliminazione del valore Bob vengono invece bloccati fino a quando non viene eseguito il commit o il rollback della transazione che esegue l'eliminazione.
È possibile eliminare intervalli utilizzando tre modalità di blocco di base, ovvero il blocco di riga, il blocco di pagina o il blocco di tabella. La strategia di blocco, ovvero il blocco a livello di pagina, di tabella o di riga, viene scelta da Query Optimizer o specificata dall'utente tramite hint di ottimizzazione, ad esempio ROWLOCK, PAGLOCK o TABLOCK. Se si utilizza PAGLOCK o TABLOCK, Motore di database rilascia immediatamente la pagina di indice se vengono eliminate tutte le righe dalla pagina. Quando invece si utilizza ROWLOCK, le righe eliminate vengono semplicemente contrassegnate come eliminate e vengono rimosse dalla pagina di indice in un momento successivo tramite un processo in background.
Operazioni di inserimento
Quando si inserisce un valore in una transazione, l'intervallo in cui è compreso non deve essere bloccato per l'intera durata della transazione che esegue l'operazione di inserimento. Per mantenere la serializzabilità è infatti sufficiente bloccare il valore della chiave inserita fino al termine della transazione. Si consideri ad esempio l'istruzione INSERT seguente:
INSERT mytable VALUES ('Dan');
Il blocco di intervalli di chiavi in modalità RangeI-N viene inserito nella voce di indice corrispondente al nome David per verificare l'intervallo. Se il blocco viene concesso, viene inserita la voce Dan e viene impostato un blocco esclusivo (X) sul valore Dan. Il blocco di intervalli di chiavi in modalità RangeI-N è necessario solo per verificare l'intervallo e non viene mantenuto attivo per l'intera durata della transazione che esegue l'operazione di inserimento. Altre transazioni possono inserire o eliminare valori prima o dopo il valore inserito Dan. Le transazioni che tuttavia tentano di leggere, inserire o eliminare il valore Dan sono bloccate fino a quando non viene eseguito il commit o il rollback della transazione di inserimento.