In SQL Server, come funzionano i blocchi di lettura?


17

Supponiamo di avere la seguente query di lunga durata

UPDATE [Table1]
SET [Col1] = 'some value'
WHERE [Col2] -- some clause which selects thousands of rows

e supponiamo che la seguente query venga eseguita mentre la query sopra è in esecuzione

SELECT *
FROM [Table1]

La prima query impedisce l'esecuzione della seconda query fino al completamento della prima query? In tal caso, la prima query impedisce l'esecuzione della seconda query su tutte le righe o solo sulle righe coinvolte nella clausola WHERE?

MODIFICARE:

Supponiamo che la seconda query sia

SELECT [Col1], [Col2]
FROM [Table1]
WHERE [Col2] -- some clause whose matching elements overlap those from
             -- the clause in the first query and which has additional matching elements

Risposte:


14

Ti consiglio di leggere Comprendere come SQL Server esegue una query , ha una spiegazione di come funzionano le letture e le scritture e come funziona il blocco.

La vista a 10000 piedi è la seguente:

  • gli operatori di lettura acquisiscono blocchi condivisi sui dati letti, prima di leggere i dati
  • gli operatori di scrittura acquisiscono blocchi esclusivi sui dati che modificano prima di modificare i dati
  • i blocchi di dati sono solo stringhe, ad es. un hash della chiave che viene letto nell'ambito dal database e dall'oggetto.
  • il gestore dei blocchi mantiene un elenco di tutti i blocchi concessi e rileva incompatibilità, secondo la matrice Compatibilità dei blocchi
  • le richieste incompatibili vengono sospese fino al rilascio della concessione incompatibile che le blocca
  • gli operatori utilizzano una gerarchia di blocchi per dichiarare l'intenzione di leggere o aggiornare i dati a livello superiore (a livello di pagina o di tabella, ignorando le opzioni a livello di partizione). Ciò consente agli operatori di bloccare intere tabelle senza bloccare ogni singola riga
  • la durata del blocco e i blocchi di intervallo vengono utilizzati per imporre livelli di isolamento più elevati

Questa è davvero solo la punta del ghiaccio. L'argomento è vasto. Nel tuo esempio, nessuno può rispondere alla tua domanda su ciò che viene effettivamente bloccato perché dipenderà da molti fattori. Ovviamente, nessuna applicazione dovrebbe emettere un SELECT * FROM Table1 perché manca una clausola WHERE e sta usando *. Queste sono cattive pratiche perché, tra le altre cose, porteranno esattamente a bloccare la contesa.

Se si verificano blocchi di lettura e scrittura, è necessario esaminare il controllo delle versioni delle righe e l'isolamento delle istantanee. Leggi Informazioni sui livelli di isolamento basati sulle versioni di riga .


Cosa succede se ho bisogno di tutto il contenuto di una tabella (supponiamo che ci siano solo 14 righe)? Come è una cattiva pratica SELECT * FROM Table1se è esattamente quello di cui ho bisogno?
Azimut,

1
*di per sé è una cattiva pratica perché quando cambia la struttura della tabella l'applicazione di solito si interrompe (di conseguenza vengono visualizzate colonne impreviste).
Remus Rusanu,

3

Modifica: Come sottolinea @MaxVernon , il seguente non è in alcun modo un suggerimento per usare NOLOCK , e avrei dovuto menzionare il settaggio del livello di transazione READ UNCOMMITEDe lasciare che la connotazione negativa rimanga lì piuttosto che far NOLOCKapparire in primo luogo. Quindi, come originariamente pubblicato:

Il rapido e semplice è "Sì, la prima query bloccherà la seconda query a meno che non venga specificato un suggerimento di indice specifico ( NOLOCK , a volte chiamato" lettura sporca ") o il livello di isolamento della transazione della seconda query sia impostato su READ UNCOMMITED(che funziona in modo identico), no non lo fa."

In risposta ai dettagli aggiuntivi forniti nella domanda che comporta l'inclusione di una WITHclausola sulla seconda SELECT, essendo reciprocamente esclusivi o meno, le interazioni tra le due query saranno sostanzialmente le stesse.

IF NOT EXISTS ( SELECT  1
                FROM    sys.objects
                WHERE   name = 'Foo'
                    AND type = 'U' )
BEGIN
    --DROP TABLE dbo.Foo;
    CREATE TABLE dbo.Foo
    (
        Foo_PK          BIGINT IDENTITY( 1, 1 ) NOT NULL,
                            PRIMARY KEY ( Foo_PK ),
        Bar             BIT,
        x               BIT,
        y               BIT,
        z               BIT
    );

    CREATE NONCLUSTERED INDEX IX_Foo_x
        ON  dbo.Foo ( x );

    INSERT INTO dbo.Foo ( Bar, x, y, z )
    VALUES ( 1, 1, 1, 1 ), ( 0, 0, 0, 0 );
END;    
GO

BEGIN TRANSACTION;

UPDATE  dbo.Foo
    SET y = 0
WHERE   x = 1;

-- COMMIT TRANSACTION;

In una sessione separata, eseguire quanto segue:

SELECT  *
FROM    dbo.Foo WITH ( NOLOCK );
GO

SELECT  *
FROM    dbo.Foo;

Puoi esaminare i blocchi attualmente in esecuzione eseguendo sp_lock, preferibilmente in un'altra sessione separata:

EXECUTE dbo.sp_lock;

Dovresti vedere un KEYblocco di tipo trattenuto dallo spid che esegue la transazione di inserimento in Xmodalità (esclusiva), da non confondere con gli altri IXblocchi (Intent-Exclusive). La documentazione del blocco indica che mentre il KEYblocco è specifico dell'intervallo, impedisce anche ad altre transazioni di inserire o aggiornare le colonne interessate modificando i dati in essa contenuti in modo che possano rientrare in quell'intervallo della query originale. Poiché il blocco stesso è esclusivo, la prima query impedisce l'accesso alla risorsa da qualsiasi altra transazione simultanea. In effetti, tutte le righe della colonna sono bloccate, indipendentemente dal fatto che rientrino o meno nell'intervallo specificato dalla prima query.

Il Sblocco che viene trattenuto dalla seconda sessione sarà quindi WAITfino a quando il Xblocco non si cancella, impedendo che un altro X(o U) blocco venga prelevato su quella risorsa da un diverso spid simultaneo prima che la seconda sessione completi l'operazione di lettura, giustificando l'esistenza del Sblocco.

Ora una modifica per chiarezza: a meno che non mi sbagli in ciò che è una lettura sporca dalla breve descrizione dei rischi menzionati qui ... Modifica 3 : Ho appena realizzato che non sto prendendo in considerazione l'effetto di un checkpoint di sfondo che scrive un di transazione su disco non ancora impegnata, quindi sì, la mia spiegazione era fuorviante.

Nella seconda query, il primo batch può (e in questo caso, restituirà) dati non impegnati. Il secondo batch, in esecuzione nel livello di isolamento della transazione predefinito di, READ COMMITEDverrà restituito solo dopo il completamento di un commit o rollback nella prima sessione.

Da qui puoi esaminare i piani di query e i livelli di blocco associati, ma meglio ancora, puoi leggere tutto sui blocchi in SQL Server qui .


1
Un avvertimento sull'uso WITH (NOLOCK)sarebbe utile in questo caso. Vedi brentozar.com/archive/2011/11/… e brentozar.com/archive/2013/02/… per ulteriori informazioni.
Max Vernon,

3
Oh, il WITH (NOLOCK)suggerimento non restituisce dalla memoria pagine sporche che non sono state salvate. In realtà legge le righe dalla tabella (sia su disco che memorizzate nella cache) senza impedire agli autori di aggiornare o aggiungere righe alle pagine utilizzate dalla tabella.
Max Vernon,

2
Non ho capito bene. Se la risposta a "la prima query impedisce l'esecuzione della seconda?" è "No", come può la risposta alla seconda domanda essere "Sì"? Puoi chiarire a quali domande stai rispondendo ed espandere le tue risposte?
Jon of All Trades,

Modifica a bizzeffe, scusate gente! Fammi sapere se c'è qualcos'altro che non è chiaro!
Avarkx,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.