Estou trabalhando com o Microsoft SQL Server 2016 (SP2-CU11) (KB4527378) - 13.0.5598.27 (X64) 27 de novembro de 2019 18:09:22 Copyright (c) Microsoft Corporation Standard Edition (64 bits) no Windows Server 2012 R2 Padrão 6.3 (Build 9600: )
Este servidor está em unidades SSD e tem uma memória máxima de 128 GB. CostTheshold para Paralelismo é 70, MaxDegree de Paralelismo é 3.
Eu tenho uma tabela "Trips" que é referenciada por 23 chaves estrangeiras com a opção ON DELETE CASCADE.
Esta tabela por si só não é tão grande (5,3 milhões de linhas, 1,3 gb de dados). Mas das 23 tabelas referenciadas, duas das tabelas são bem grandes (mais de 1 bilhão de linhas, 54 e 69 gb cada).
O problema é que quando tentamos excluir uma pequena quantidade de linhas na tabela "Trips" (digamos 4 linhas), o SQL estima que tantas linhas serão excluídas, ele pede 10 GB de RAM, estima que milhões de linhas serão retornado e bloqueia a tabela. Tudo para e outras consultas são bloqueadas e o aplicativo expira.
Aqui estão as tabelas principais e a contagem de linhas para 1 instrução de exclusão:
- Viagens (4 linhas)
- Segmentos (27 linhas, relacionadas a Viagens por SegmentId)
- Perfis (linhas de 2012, relacionadas a Segmentos por SegmentId)
- ProfileRanges (2337 linhas, relacionadas a perfis por ProfileId)
- Eventos (7.750 linhas, relacionadas a segmentos por SegmentId)
- EventConditions (9230 linhas, relacionadas a eventos por EventId)
As tabelas EventConditions e ProfileRanges têm mais de 1 bilhão de linhas cada.
Aqui está o cache do plano: https://www.brentozar.com/pastetheplan/?id=HJNg5I0BU
Quando olho no explorador de planos SentryOne, posso ver que o SQL está lendo a tabela inteira, mesmo que o "spool de tabela" filtre e mantenha apenas para 2012 linhas ProfileRanges e quase o mesmo para EventConditions.
Quando olho para a concessão de memória da consulta com o procedimento sp_blitzCache de Brent Ozar, posso ver que a consulta pede cerca de 10 GB de RAM.
Depois disso, a consulta está esperando em SOS_SCHEDULER_YIEL (então esperando a vez de usar a CPU após os 4ms) ou MEMORY_ALLOCATION_EXT. O programa expira e falha.
O que posso fazer para que isso funcione?
Uma das coisas que eu estava pensando era remover as chaves estrangeiras nas duas maiores tabelas e excluir suas linhas em um gatilho em vez de. Mas eu não sou um grande fã de impor a consistência do banco de dados com gatilhos em vez de chaves estrangeiras.
Qualquer conselho ou ajuda será apreciado
A chave primária de ProfileRanges é
- PerfilId int
- ProfileRangeDefId1 int
- ProfileRangeDefId2 int
A chave primária de EventConditions é
- EventId bigint
- EventConditionDefId int