SELECIONAR / INSERIR Deadlock

10

Esta instância hospeda os bancos de dados do SharePoint 2007 (SP). Temos experimentado vários bloqueios SELECT / INSERT em uma tabela muito utilizada no banco de dados de conteúdo do SP. Eu reduzi os recursos envolvidos, ambos os processos estão exigindo bloqueios no índice não clusterizado.
O INSERT precisa de um bloqueio IX no recurso SELECT e o SELECT precisa de um bloqueio S no recurso INSERT. O gráfico de deadlock representa e três recursos, 1.) dois do SELECT (encadeamentos paralelos produtor / consumidor) e 2.) o INSERT.
Anexei o gráfico de deadlock para sua revisão. Como essas são estruturas de código e tabela da Microsoft, não podemos fazer alterações.
No entanto, li no site do MSFT SP que eles recomendam definir a opção de configuração no nível da instância MAXDOP como 1. Como essa instância é compartilhada com muitos outros bancos de dados / aplicativos, essa configuração não pode ser desativada.


Portanto, decidi tentar impedir que essas instruções SELECT fiquem paralelas. Eu sei que isso não é uma solução, mas mais uma modificação temporária para ajudar na solução de problemas. Portanto, aumentei o “Limiar de custo para paralelismo” de nosso padrão de 25 para 40 ao fazer isso, mesmo que a carga de trabalho não tenha sido alterada (SELECT / INSERT ocorrendo com freqüência) e os impasses desapareceram. Minha pergunta é por que?

O SPID 356 INSERT possui um bloqueio IX em uma página pertencente ao índice não agrupado. O
SPID 690 SELECT Execution ID 0 possui um bloqueio S em uma página pertencente ao mesmo índice não agrupado.

Agora

O SPID 356 deseja um bloqueio IX no recurso SPID 690, mas não pode obtê-lo porque o SPID 356 está sendo bloqueado pelo SPID 690 Execution ID 0 S lock O
SPID 690 Execution ID 1 deseja um bloqueio S no recurso SPID 356, mas não pode obtê-lo porque o SPID 690 Execution ID 1 está sendo bloqueado pelo SPID 356 e agora temos nosso impasse.

Plano de execução pode ser encontrado no meu SkyDrive

Detalhes completos do impasse podem ser encontrados aqui

Se alguém pode me ajudar a entender por que eu realmente aprecio isso.

Tabela EventReceivers.
Id uniqueidentifier não 16
Nome nvarchar não 512
SiteId uniqueidentifier não 16
WebId uniqueidentifier não 16
HostId uniqueidentifier não 16
HostType int não 4
ItemId int não 4
DirName nvarchar não 512
LeafName nvarchar não 256
Type int não 4
SequenceNumber int não 4
Assembly nvarchar não 512
Class nvarchar não 512
nvarchar de dados não 512
filtro nvarchar não 512
SourceId tContentTypeId não 512
SourceType int não 4
Credential int não 4
ContextType varbinary não 16
ContextEventType varbinary no 16
ContextId varbinary no 16
ContextObjectId varbinary no 16
ContextCollectionId varbinary no 16

index_name index_description index_keys
EventReceivers_ByContextCollectionId nonclustered localizado na PRIMÁRIA SiteID, ContextCollectionId
EventReceivers_ByContextObjectId NONCLUSTERED localizado na PRIMÁRIA SiteID, ContextObjectId
EventReceivers_ById NONCLUSTERED, único, localizado em PRIMÁRIA SiteID, Id
EventReceivers_ByTarget cluster, único, localizado em PRIMÁRIA SiteID, webid, HOSTID, HostType, Type, ContextCollectionId, ContextObjectId, ContextId, ContextType, ContextEventType, SequenceNumber, Assembly, Classe
EventReceivers_IdUnique chave não clusterizada, exclusiva e exclusiva, localizada na ID PRIMARY

SQLJarHead
fonte
2
O que fazer proc_InsertEventReceivere proc_InsertContextEventReceiverfazer que não podemos ver no XDL? Também para reduzir o paralelismo, por que não impactar essas instruções diretamente (usando o MAXDOP 1) em vez de futzing com as configurações do servidor?
Aaron Bertrand
11
Estou curioso para saber qual é a sua configuração MAXDOP ampla e quantos processadores (lógicos) você possui. O SharePoint realmente funciona melhor e prefere estar em um servidor com um servidor MAXDOP com 1 largura. Não gosto, mas foi assim que eles o desenvolveram. Você pode postar os planos de execução reais? Tudo o que vejo nesse link é o .xdl (gráfico de conflito) #
Mike Walsh
Olá Senhores, eu realmente aprecio o seu tempo, fora de sua agenda lotada para responder. Vou postar os procedimentos e os planos de execução para sua revisão no site do SkyDrive. Pensei em modificar o código para incluir a opção de consulta MAXDOP (1), no entanto, isso invalidará nosso suporte com a Microsoft. O servidor físico é uma configuração ProLiant DL580 G4 MAXDOP é 4 com um total de 8 processadores físicos e o H / T está desativado.
SQLJarHead
Olá Senhores, Criei um pacote zip com todos os detalhes no SkyDrive. Modifiquei o corpo da postagem original para incluir o URL do pacote. Por favor, não me diga qual é o problema, apenas forneça orientação e faça com que eu trabalhe para ele. NOTA: Não consigo fazer alterações de código ou DDL no esquema subjacente.
SQLJarHead
11
Portanto, você não pode alterar o código e o esquema, que outras soluções você espera que apresentemos? Se você está preocupado em anular o suporte da Microsoft, isso implica que você possui o suporte da Microsoft; nesse caso - dadas todas as restrições que você nos disse que não pode fazer -, você considerou abrir um tíquete de suporte com a Microsoft?
Aaron Bertrand

Respostas:

14

Em face disso, isso parece um impasse de pesquisa clássica . Os ingredientes essenciais para esse padrão de impasse são:

  • uma SELECTconsulta que usa um índice não clusterizado sem cobertura com uma Pesquisa de Chave
  • uma INSERTconsulta que modifica o índice em cluster e, em seguida, o índice não clusterizado

Ele SELECTacessa o índice não clusterizado primeiro e, em seguida, o índice clusterizado. O INSERTacesso ao índice clusterizado primeiro, depois ao índice não clusterizado. Acessar os mesmos recursos em uma ordem diferente e adquirir bloqueios incompatíveis é uma ótima maneira de "alcançar" um impasse, é claro.

Nesse caso, a SELECTconsulta é:

Consulta SELECT

... e a INSERTconsulta é:

Consulta INSERT

Observe a manutenção de índices não agrupados em destaque em verde.

Nós precisaríamos ver a versão serial do SELECTplano, caso ela seja muito diferente da versão paralela, mas, como Jonathan Kehayias observa em seu guia para Manipulação de deadlocks , esse padrão de deadlock específico é muito sensível ao tempo e aos detalhes da implementação da execução de consultas internas. Esse tipo de conflito geralmente ocorre sem uma razão externa óbvia.

Dado o acesso ao sistema em questão e as permissões adequadas, estou certo de que poderemos eventualmente descobrir exatamente por que o conflito ocorre com o plano paralelo, mas não com o serial (assumindo a mesma forma geral). Possíveis linhas de investigação incluem a verificação de laços aninhados otimizadas e / ou pré-busca - o que pode internamente aumentar o nível de isolamento para REPEATABLE READpara a duração da declaração. Também é possível que algum recurso da atribuição de intervalo de busca de índice paralelo contribua para o problema. Se o plano em série estiver disponível, talvez eu passe algum tempo analisando os detalhes mais detalhadamente, pois é potencialmente interessante.

A solução usual para esse tipo de impasse é tornar a cobertura do índice, embora o número de colunas nesse caso possa torná-lo impraticável (e, além disso, não devemos mexer com essas coisas no SharePoint, me disseram). Por fim, a recomendação para planos somente em série ao usar o SharePoint existe por um motivo (embora não necessariamente bom, quando se trata disso). Se a mudança no limiar de custo para o paralelismo resolver o problema no momento, isso é bom. A longo prazo, eu provavelmente procuraria separar as cargas de trabalho, talvez usando o Administrador de Recursos, para que as consultas internas do SharePoint obtenham o MAXDOP 1comportamento desejado e o outro aplicativo possa usar o paralelismo.

A questão das trocas que aparecem no traço do impasse parece um arenque vermelho para mim; simplesmente uma conseqüência dos encadeamentos independentes que possuem recursos que tecnicamente devem aparecer na árvore. Não vejo nada que sugira que as próprias trocas estejam contribuindo diretamente para a questão do impasse.

Paul White 9
fonte
6

Se esse foi um impasse de pesquisa clássica , a lista de recursos incluirá o Índice em Cluster e o Índice Não em Cluster. Normalmente, o SELECT retém um bloqueio SHARED no índice NC e aguarda um bloqueio SHARED no IC, enquanto o INSERT adquire um bloqueio EXCLUSIVO no IC e aguarda um bloqueio EXCLUSIVO no NC. A lista de recursos no xml de deadlock listará esses dois objetos nesse caso.

Como o gráfico de deadlock envolve apenas o NC Index, podemos descartar essa opção.

Além disso, se esse foi um bloqueio morto devido à junção de loop aninhado com UNORDERED PREFETCH , o plano de execução nos dirá se o algoritmo UNORDERED PREFETCH é usado, o que novamente não é o caso aqui (consulte a atualização abaixo).

Isso nos deixa supor que este é um impasse devido ao plano paralelo.

O gráfico de deadlock não é renderizado corretamente, mas se você observar o XML do deadlock, poderá ver que dois threads da instrução SELECT (SPID 690) estão envolvidos no deadlock. O encadeamento do consumidor está mantendo um bloqueio SHARED na PÁGINA 1219645 e aguardando o produtor na porta801f8ed0 (e_waitPipeGetRow). O encadeamento do produtor está aguardando um bloqueio compartilhado na PAGE 1155940.

A instrução INSERT está mantendo um bloqueio IX na PÁGINA 1155940 e aguardando um bloqueio IX na PÁGINA 1219645, resultando em um impasse.

Acredito que um impasse será evitado ao usar um plano serial para a instrução SELECT, pois em nenhum momento ele exigirá bloqueio SHARED em mais de uma página. Eu também acho que o plano serial será quase o mesmo que o plano paralelo (sem o operador paralelismo).

[ATUALIZADO com base no comentário de Paul]

Aparentemente, o plano está usando um algoritmo OPTIMIZED Nested Loop

Isso explica por que os bloqueios SHARED são mantidos até o final da instrução. REPEATABLE READ combinado com o plano paralelo é mais vulnerável ao conflito do que um plano serial porque o plano paralelo pode adquirir e manter bloqueios de diferentes intervalos de um índice, enquanto o plano serial adquire bloqueios de maneira mais seqüencial.

Roji P Thomas
fonte
Acordado. Se esse conflito estivesse relacionado a uma consulta real, o recurso de espera, para o SELECT, faria referência ao índice em cluster. Consegui descartar isso exibindo o cabeçalho da página para cada recurso de espera (SPID 690 Wait Resource = PAGE: 1155940 | SPID 356 Wait Resource = PAGE 1219645) via DBCC PAGE e ambos estavam no índice ID 5 (IndexID 5 = EventReceivers_ByContextObjectId) que aponta para o índice NC na tabela especificada (EventReceivers).
SQLJarHead 6/12/12
Senhores, obrigado novamente por dedicar um tempo para ajudar a analisar esta questão interessante. Algumas perguntas: 1.) Roji ressalta que o SPID paralelo está solicitando mais de uma página. Não vejo isso em nenhum dos planos de execução. Observando o número de linhas, para o operador INDEX SEEK, apenas um encadeamento dos dois produtores está processando todas as linhas. Como você determinou que estava solicitando mais de uma página? (1/2)
SQLJarHead 6/12
2.) Um algoritmo OTIMIZED Nested Loop sempre definirá o nível de isolamento como REAPTABLE READ? Verifiquei a saída XML dos planos de execução e só vejo a leitura confirmada para a conexão SPIDs. Estou assumindo que isso é invocado apenas no nível do operador do plano. (2/2)
SQLJarHead 6/12/12
O comportamento de bloqueio de OPTIMIZED Nested Loops é comparável a REPEATABLE READ (mantém os bloqueios até o final da instrução ), mas não explicitamente define o nível de isolamento da transação como REPEATABLE READ. Eu acho que isso responde à sua pergunta também. Não é que os threads paralelos estejam solicitando mais de uma página por vez, mas um thread paralelo esteja mantendo um bloqueio em uma página e outro esteja esperando por um bloqueio em outra página
Roji P Thomas