Eu TABLOCK
editei uma tabela para poder executar inserções em massa. Como posso lê-lo enquanto ainda está bloqueado?
Em circunstâncias normais, eu consideraria isso obviamente impossível. Felizmente, há um fator-chave que torna as circunstâncias anormais. Nesse caso, não me importo nem um pouco com ACID . A única coisa que me importa é não cancelar a consulta que editou TABLOCK
a tabela.
Eu tentei WITH (NOLOCK)
, mas os sinais sugerem que não funciona. Estou pedindo o impossível?
Só para esclarecer, suponha que eu esteja correndo
INSERT [dbo].[Table1] WITH (TABLOCK)
SELECT * FROM [A_Lot_Of_Rows]
Meu objetivo é consultar Table1
enquanto isso é executado.
Estou fazendo inserções em massa e, enquanto elas estão rodando, decidi que quero verificar alguns detalhes. Eu não me importo de qual linha eu recebo esses detalhes.
Cada linha A_Lot_Of_Rows
contém uma data. A_Lot_Of_Rows
é truncado pelo meu procedimento armazenado e é preenchido novamente em lotes baseados em data antes de ser truncado novamente. Ao ler uma linha arbitrária da tabela, posso saber em que lote estou.
Existem duas possibilidades:
1. Truncando dentro de uma transação
Isso se aplica se você estiver truncando a tabela e depois carregando em lotes dentro de uma transação .
Truncar uma tabela requer um bloqueio de modificação de esquema (Sch-M), que é o tipo de bloqueio mais restritivo . Mesmo a leitura no nível de isolamento de leitura não confirmada (alias enganoso de 'nolock') ou RCSI requer um bloqueio de estabilidade de esquema (Sch-S), que não é compatível com Sch-M.
Uma dica
TABLOCK
ouTABLOCKX
normalmente não impede a leitura de dados sob isolamento de leitura não confirmada (mas consulte a possibilidade nº 2).Trunque a tabela fora da transação de carregamento de dados ou registre seu progresso separadamente e consulte-o.
É lamentável que o truncamento de uma tabela exija um bloqueio Sch-M, enquanto o equivalente irrestrito
DELETE
não, mas é assim que é implementado.2. A tabela está agrupada
Se a tabela tiver um índice clusterizado, há uma diferença importante ao usar um bloqueio de tabela na
INSERT
instrução (ou qualquer outra maneira de especificar um bloqueio de tabela no carregamento em massa):RowsetBulk
, que é essencialmente um índice offline criado nos bastidores. Isso requer um bloqueio Sch-M . Nenhuma leitura simultânea é possível neste caso.FastLoadContext
mecanismo. Isso exige um bloqueio exclusivo ('X') na tabela de destino, mas não Sch-M . Leituras simultâneas sob isolamento de leitura não confirmada ou RCSI são possíveis neste caso.O
FastLoadContext
caminho registra um pouco maisRowsetBulk
e pode ser visivelmente mais lento, mas ainda é tecnicamente minimamente registrado e mais eficiente do que uma operação totalmente registrada. Isso tudo pressupõe que seu banco de dados e configuração se qualifiquem para registro mínimo .Há outra peculiaridade :
FastLoadContext
os cálculos realizados pelo SQL Server para verificar se o log mínimo deve ser aplicado não levam em conta quaisquer alterações na cardinalidade da tabela no lote atual (procedimento armazenado no seu caso).Isso significa que o SQL Server pode decidir não registrar minimamente porque não percebe que a tabela foi esvaziada pela
TRUNCATE TABLE
instrução no mesmo procedimento. Que eu saiba, não há nenhuma solução alternativa específica para isso, além de executar o truncamento em um lote separado.Se você conseguir capturar planos de execução reais para a inserção, verifique se o operador Clustered Index Insert possui a propriedade DMLRequestSort definida como true. A reutilização do plano em cache também pode ser um fator aqui, que uma
OPTION (RECOMPILE)
dica pode ajudar a evitar.Tenha também em mente que a primeira página de dados escritos usando
FastLoadContext
é sempre totalmente registrada. Mais detalhes em meus artigos vinculados acima.Também é possível ver diferentes comportamentos de bloqueio ao carregar um heap em massa, mas isso depende da versão do SQL Server que você está usando. Consulte a seção intitulada "Carregamento em massa, consultas NOLOCK e isolamento de instantâneo confirmado de leitura" no Guia de desempenho de carregamento de dados (alguns detalhes não se aplicam às versões atuais).
Existem dois tipos de bloqueios de tabela. O bloqueio de tabela DML, que é um bloqueio X no objeto, como se você adicionasse uma dica de tabela TABLOCKX, e o bloqueio de esquema exclusivo SCH-M, obtido ao alterar o truncamento ou a troca de partição em uma tabela.
READ UNCOMMITTED/NOLOCK lerá um TABLOCKX, mas não um bloqueio SCH-M.