No SQL Server, como funcionam os bloqueios de leitura?

17

Suponha que eu tenha a seguinte consulta de longa duração

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

e suponha que a consulta a seguir seja executada enquanto a consulta acima estiver em execução

SELECT *
FROM [Table1]

A primeira consulta impede a execução da segunda consulta até que a primeira seja concluída? Nesse caso, a primeira consulta impede que a segunda consulta seja executada em todas as linhas ou apenas nas linhas envolvidas na cláusula WHERE?

EDITAR:

Suponha que a segunda consulta seja

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
cm007
fonte

Respostas:

14

Eu recomendo que você leia Noções básicas sobre como o SQL Server executa uma consulta , ele tem uma explicação de como as leituras e gravações funcionam e como o bloqueio funciona.

A visualização de 10000 pés é a seguinte:

  • operadores de leitura adquirem bloqueios compartilhados nos dados que lêem, antes de ler os dados
  • operadores de gravação adquirem bloqueios exclusivos nos dados que eles modificam antes de modificar os dados
  • bloqueios de dados são apenas cadeias de caracteres, por exemplo. um hash da chave que está sendo lido no escopo pelo banco de dados e objeto.
  • o gerenciador de bloqueios mantém uma lista de todos os bloqueios concedidos e detecta incompatibilidades, de acordo com a matriz de compatibilidade de bloqueios
  • solicitações incompatíveis são suspensas até que a concessão incompatível que os bloqueie seja liberada
  • Os operadores usam uma hierarquia de bloqueio para declarar a intenção de ler ou atualizar dados em nível superior (nível de página ou tabela, ignorando as opções de nível de partição). Isso permite que os operadores bloqueiem tabelas inteiras sem bloquear cada linha individual
  • vida útil do bloqueio e bloqueios de intervalo são usados ​​para impor níveis mais altos de isolamento

Esta é realmente apenas a ponta do iceberg. O assunto é vasto. No seu exemplo, ninguém pode responder à sua pergunta sobre o que está realmente sendo bloqueado, porque isso dependerá de muitos fatores. Obviamente, nenhum aplicativo deve emitir um SELECT * FROM Table1 porque está faltando uma cláusula WHERE e está sendo usado *. Essas são práticas ruins porque, entre outras coisas, levarão exatamente a travar contendas.

Se você encontrar bloqueios de leitura versus gravação, precisará examinar o controle de versão de linha e o instantâneo. Leia Noções básicas sobre os níveis de isolamento com base no controle de versão de linha .

Remus Rusanu
fonte
E se eu precisar de todo o conteúdo de uma tabela (digamos que tenha apenas 14 linhas)? Como é uma má prática SELECT * FROM Table1se é exatamente isso que eu preciso?
Azimute
1
*por si só, é uma prática ruim porque, quando a estrutura da tabela é alterada, o aplicativo geralmente quebra (colunas inesperadas aparecem no resultado).
Remus Rusanu 15/03
3

Edit: Como o @MaxVernon aponta, o seguinte não é de forma alguma uma sugestão para usar o NOLOCK , e eu muito bem deveria ter acabado de mencionar definir o nível da transação READ UNCOMMITEDe deixar a conotação negativa lá do que mencionar NOLOCKem primeiro lugar. Então, como publicado originalmente:

O mais rápido e simples é "Sim, a primeira consulta bloqueará a segunda consulta, a menos que uma dica específica do índice seja especificada ( NOLOCK , às vezes chamada de" leitura suja ") ou o nível de isolamento da transação da segunda consulta esteja definido como READ UNCOMMITED(que opera de forma idêntica), não, não tem."

Em resposta aos detalhes adicionais fornecidos na pergunta que envolvem a inclusão de uma WITHcláusula na segunda SELECT, sendo mutuamente exclusivos ou não, as interações entre as duas consultas serão praticamente as mesmas.

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;

Em uma sessão separada, execute o seguinte:

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

SELECT  *
FROM    dbo.Foo;

Você pode examinar os bloqueios atualmente em execução executando sp_lock, de preferência em mais uma sessão separada:

EXECUTE dbo.sp_lock;

Você verá um KEYbloqueio de tipo sendo retido pelo spid executando a transação de inserção no modo X(exclusivo), para não ser confundido com os outros IXbloqueios (exclusivo de intenção). A documentação do bloqueio indica que, embora o KEYbloqueio seja específico do intervalo, ele também impede que outras transações insiram ou atualizem as colunas afetadas, alterando os dados nela contidos, para que possam ficar dentro desse intervalo da consulta original. Como o próprio bloqueio sendo mantido é exclusivo, a primeira consulta está impedindo o acesso ao recurso de qualquer outra transação simultânea. Com efeito, todas as linhas da coluna estão bloqueadas, estejam ou não dentro do intervalo especificado pela primeira consulta.

O Sbloqueio que está sendo mantido pela segunda sessão será assim WAITaté que o Xbloqueio seja liberado, impedindo que outro X(ou U) bloqueio seja recebido nesse recurso de um spid simultâneo diferente antes que a segunda sessão conclua sua operação de leitura, justificando a existência do Sbloqueio.

Agora, uma edição para maior clareza: a menos que eu esteja enganado no que é uma leitura suja, a partir da breve descrição dos riscos mencionados aqui ... Editar 3 : Acabei de perceber que não estou considerando o efeito de um ponto de verificação de plano de fundo que escreve como de transação ainda não confirmada em disco, então sim, minha explicação foi enganosa.

Na segunda consulta, o primeiro lote pode (e, nesse caso,) retornar dados não confirmados. O segundo lote, em execução no nível de isolamento de transação padrão READ COMMITED, retornará somente depois que uma confirmação ou reversão for concluída na primeira sessão.

A partir daqui, você pode examinar seus planos de consulta e os níveis de bloqueio associados, mas, melhor ainda, pode ler tudo sobre bloqueios no SQL Server aqui .

Avarkx
fonte
1
Uma palavra de aviso sobre o uso WITH (NOLOCK)seria útil neste caso. Veja brentozar.com/archive/2011/11/… e brentozar.com/archive/2013/02/… para mais informações.
Max Vernon
3
Ah, a WITH (NOLOCK)dica não retorna páginas sujas da memória que não foram confirmadas. Na verdade, ele lê as linhas da tabela (em disco ou armazenadas em cache na memória) sem impedir que os gravadores atualizem ou adicionem linhas às páginas usadas pela tabela.
Max Vernon
2
Estou confuso. Se a resposta para "a 1ª consulta impedir a execução da 2ª?" é "Não", como a resposta para a segunda pergunta pode ser "Sim"? Você pode esclarecer quais perguntas está respondendo e expandir suas respostas?
Jon of All Trades
Edições em abundância, desculpe pessoal! Deixe-me saber se há algo mais que não esteja claro!
Avarkx 20/01