Dica do Tablock aciona deadlocks

10

Eu estava inserindo dois conjuntos de dados, usando o mínimo de log, em uma tabela de heap vazia usando duas tarefas SQL de execução em execução paralela e com o SQL do seguinte formulário.

INSERT INTO Table (TABLOCK) SELECT FROM ...

Depois que o trabalho é interrompido um pouco, uma das tarefas do SQL se tornou uma vítima de conflito. Abaixo está a saída XML do gráfico de deadlock.

Alguém pode explicar o que estava acontecendo sob o capô?

  <resource-list>
   <objectlock lockPartition="0" objid="1586156746" subresource="FULL" dbid="7" objectname="dbo.TargetTable" id="lock7374a00" mode="IX" associatedObjectId="1586156746">
    <owner-list>
     <owner id="process9609dc8" mode="Sch-S"/>
     <owner id="process9609dc8" mode="IX"/>
    </owner-list>
    <waiter-list>
     <waiter id="process5e13048" mode="X" requestType="convert"/>
    </waiter-list>
   </objectlock>
   <objectlock lockPartition="0" objid="1586156746" subresource="FULL" dbid="7" objectname="dbo.TargetTable" id="lock7374a00" mode="IX" associatedObjectId="1586156746">
    <owner-list>
     <owner id="process5e13048" mode="Sch-S"/>
     <owner id="process5e13048" mode="IX"/>
    </owner-list>
    <waiter-list>
     <waiter id="process9609dc8" mode="X" requestType="convert"/>
    </waiter-list>
   </objectlock>
  </resource-list>

As coisas ficam muito mais complicadas porque descobri que, na maioria dos casos, as duas tarefas Executar SQL podem ser executadas em paralelo com êxito. Tente abaixo:

Create table dbo.TablockInsert (c1 int, c2 int, c3 int)

--then issue the script in two Execute Sql Task in parallel you won't fail:
insert into dbo.TablockInsert(TABLOCK) SELECT 1, 1, 1

Como a única diferença é a instrução SELECT ... FROM ..., parece que a instrução SELECT ... FROM ... pode afetar o modo de bloqueio aqui?

SqlWhale
fonte
Você pode especificar TABLOCKX em vez de TABLOCK para evitar o conflito. Embora isso também serialize o acesso à tabela, você ainda obterá um log mínimo.
Dan Guzman

Respostas:

8

O Guia de Desempenho de Carregamento de Dados foi escrito para o SQL Server 2008, mas, até onde posso dizer, a Microsoft não fez nenhum aprimoramento nesta área para montes. Aqui está uma cotação para o seu cenário de carregamento:

Carregamento em massa de uma tabela vazia e não particionada

O carregamento de dados em uma tabela não particionada, embora seja uma operação simples, pode ser otimizado de várias maneiras.

...

Várias operações de inserção simultânea para heaps são possíveis apenas quando o método em massa escolhido emite bloqueios de atualização em massa (BU) na tabela. Dois bloqueios de atualização em massa (BU) são compatíveis e, portanto, duas operações em massa podem ser executadas ao mesmo tempo.

Nesse cenário, tanto INSERT… SELECT quanto SELECT INTO têm uma desvantagem. Ambas as operações usam um bloqueio exclusivo no nível da tabela (X) no destino. Isso significa que apenas uma operação de carregamento em massa pode ser executada em um determinado momento, limitando a escalabilidade. No entanto, BCP, BULK INSERT e Integration Services podem executar bloqueios de atualização em massa (BU) - se você especificar a dica TABLOCK.

A parte importante é que você não recebe um bloqueio de BU INSERT ... SELECT. Você sempre terá um bloqueio exclusivo sobre a mesa, para que apenas um INSERTpossa ser executado por vez.

Nos comentários, você disse que inserirá 100k linhas ou menos e que outros processos não serão executados nas tabelas durante as inserções. Ao enviar duas consultas INSERT para o banco de dados, esperaria que uma das três coisas acontecesse:

  1. Uma inserção é executada primeiro e bloqueia a outra inserção. A segunda inserção aguarda até que a primeira inserção seja concluída.
  2. Uma inserção termina antes do início da segunda inserção. Não há bloqueio explícito, mas eles não são executados simultaneamente.
  3. Você obtém um impasse e apenas uma inserção é concluída com êxito.

Em todos os casos, você se beneficia ou não se machuca adicionando uma TABLOCKXdica à consulta, de modo que é minha recomendação de solucionar o impasse. Se você quiser saber por que o impasse às vezes acontece, precisará procurar outra resposta para isso.

Para um cenário diferente em que você realmente precisa de inserção paralela, duas maneiras de solucionar o problema da BU são particionar seu heap e inserir cada sessão em uma partição separada ou carregar seus dados através do BCP, BULK INSERT ou Integration Services .

Joe Obbish
fonte
Obrigado pela resposta, mas a situação que encontrei em conflito é o único caso que tenho até agora. Na maioria dos casos, quando você emitir INSERT INTO WITH (TABLOCK) SELECT FROM em paralelo, o trabalho não falhará. BTW, estou usando o SQL SERVER 2008 R2. Eu adicionei um exemplo de sucesso na minha pergunta.
SqlWhale
@tec nos casos em que não falha, provavelmente eles acabam sendo executados em série. Talvez você atinja o problema apenas quando houver um certo tempo de inicialização, por exemplo, para a compilação do plano.
Martin Smith
@MartinSmith A consulta em que encontrei o problema no projeto é muito mais complexa do que o exemplo SELECT 1, que requer mais sobrecarga de compilação, mas está apenas lendo algumas tabelas. Estou tentando reproduzir esse tipo de impasse por consultas mais complexas.
SqlWhale
@TecKnowNothing Sobre quantas linhas você insere? Quantas vezes por dia o processo é executado? Outras consultas SELECT da tabela enquanto os dados são carregados?
Joe Obbish
@ JoeObbish 1. Eu não acho que o número de linhas seja um problema aqui, temos apenas 4000 - 70000 para cada consulta e, mas eles são dados altamente agregados para o uso do cubo. 2. Ainda está em fase de teste, que deve ser executada uma vez por dia. 3.Nada mais está lendo da tabela de destino.
SqlWhale
4

Você está inserindo dbo.TargetTablea partir de duas sessões e ambos usando TABLOCKhint.Both process9609dc8e process5e13048detenção processo Sch-Se IXfechaduras que são compatíveis uns com os outros para que ambos processo pode realizar ao mesmo tempo. Mas ambos querem converter IXbloqueio em Exclusive Xtipo. Xbloqueios não são compatíveis entre si. Portanto, o SQL Server escolheu uma das sessões como vítima de impasse em vez de esperar infinitamente uma pela outra.

Informações básicas de conflito.

Gráfico de Compatibilidade de Bloqueios (Mecanismo de Banco de Dados).

Detectando e encerrando impasses.

SqlWorldWide
fonte