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
fonte
proc_InsertEventReceiver
eproc_InsertContextEventReceiver
fazer 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?Respostas:
Em face disso, isso parece um impasse de pesquisa clássica . Os ingredientes essenciais para esse padrão de impasse são:
SELECT
consulta que usa um índice não clusterizado sem cobertura com uma Pesquisa de ChaveINSERT
consulta que modifica o índice em cluster e, em seguida, o índice não clusterizadoEle
SELECT
acessa o índice não clusterizado primeiro e, em seguida, o índice clusterizado. OINSERT
acesso 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
SELECT
consulta é:... e a
INSERT
consulta é:Observe a manutenção de índices não agrupados em destaque em verde.
Nós precisaríamos ver a versão serial do
SELECT
plano, 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 READ
para 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 1
comportamento 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.
fonte
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.
fonte