O SQL Server 2014 CTP1 apresenta opções de espera de bloqueio de baixa prioridade para uso com operações de índice online e comutadores de partição.
Para aqueles que utilizam o gerenciamento de índice online ou particionamento de índice e operações de alternância de partição no SQL Server 2012 Enterprise Edition, você pode ter experimentado em um ponto o bloqueio de sua operação DDL, pois essas operações ainda têm alguns requisitos de bloqueio.
Para ilustrar, imagine que eu execute a seguinte reconstrução de índice online de partição única no SQL Server 2014 CTP1:
ALTER INDEX [ClusteredIndex_on_ps_ShipDate] ON [dbo].[FactInternetSales] REBUILD PARTITION = (37) WITH (ONLINE= ON);
E vamos dar uma olhada nos bloqueios adquiridos e liberados durante esta operação de reconstrução usando Extended Events e a seguinte definição de sessão (esta é uma sessão sem destino e observei os resultados por meio do painel “Watch Live Data” no SQL Server Management Studio):
CREATE EVENT SESSION [Online_Index_Rebuild_Locks_Taken] ON SERVER ADD EVENT sqlserver.lock_acquired( WHERE ([object_id]=(309576141))), ADD EVENT sqlserver.lock_released( WHERE ([object_id]=(309576141))) WITH ( MAX_MEMORY=4096 KB, EVENT_RETENTION_MODE=ALLOW_SINGLE_EVENT_LOSS, MAX_DISPATCH_LATENCY=30 SECONDS, MAX_EVENT_SIZE=0 KB, MEMORY_PARTITION_MODE=NONE, TRACK_CAUSALITY=OFF, STARTUP_STATE=OFF ); GO
O valor 309576141 representa a identificação do objeto da tabela FactInternetSales.
Minha reconstrução de índice online de uma única partição levou 56 segundos para ser concluída e, após a conclusão, vi a seguinte atividade de aquisição e liberação de bloqueio:
Atividade de bloqueio para reconstrução online de partição única
Como você pode ver na saída, embora a reconstrução seja uma operação online, ela envolve a aquisição de bloqueios em vários modos durante o ciclo de vida da operação. Idealmente, a duração do bloqueio é mínima (por exemplo – o carimbo de data/hora é idêntico para o primeiro
SCH_S
bloqueio adquirido e liberado). Mas mesmo com uma quantidade mínima de bloqueio, você certamente pode encontrar problemas de simultaneidade, dependendo das transações executadas no índice que está sendo reconstruído ou alterado. Mencionei no início deste post que a Microsoft introduziu opções de espera de bloqueio de baixa prioridade para operações online e operações de alternância de partição no SQL Server 2014 CTP1. Sobre o assunto de comutadores de partição, imagine que eu execute a seguinte operação:
ALTER TABLE [AdventureWorksDW2012].[dbo].[FactInternetSales] SWITCH PARTITION 37 TO [AdventureWorksDW2012].[dbo].[staging_FactInternetSales];
Para ver os bloqueios adquiridos e liberados para esta operação, modifiquei minha sessão Extended Event definida anteriormente para incluir os objetos aplicáveis (tabela de origem e destino). Eu vi o seguinte:
Atividade de bloqueio para uma operação de troca de partição
A operação de troca para uma partição vazia ocorreu em menos de um segundo, mas ainda vemos que
SCH_S
e SCH_M
bloqueios eram necessários durante o ciclo de vida da operação na origem e no destino (309576141 sendo FactInternetSales e 398624463 sendo staging_FactInternetSales). Então, novamente, embora a duração do bloqueio possa ser extremamente breve quando não há transações simultâneas acessando os objetos em questão, sabemos que isso nem sempre é possível e, portanto, nossas operações de reconstrução de índice online e troca de partição podem realmente ser bloqueadas.
Assim, com essa realidade, o SQL Server 2014 apresenta o
WAIT_AT_LOW_PRIORITY
argumento que pode ser ajustado com MAX_DURATION
e ABORT_AFTER_WAIT
opções para o ALTER INDEX
e ALTER TABLE
comandos que podemos usar tanto para indexação online quanto para operações de alternância de partição. O que isso nos permite fazer? Antes de tudo, vamos falar sobre como era o comportamento antes do SQL Server 2014. Como exemplo, imagine que eu tenha a seguinte transação aberta e não confirmada:
BEGIN TRANSACTION; DELETE [dbo].[staging_FactInternetSales];
Se eu tentasse executar um
ALTER TABLE SWITCH
para a tabela staging_FactInternetSales como destino em uma sessão separada, serei bloqueado e a solicitação ficará apenas esperando. Especificamente para este exemplo, eu estaria esperando com um LCK_M_SCH_M
tipo de espera. Depois de reverter ou confirmar minha transação, a operação pode avançar e ser concluída. Agora, se estou usando o
WAIT_AT_LOW_PRIORITY
do SQL Server 2014 com MAX_DURATION
e ABORT_AFTER_WAIT
, posso aproveitar algumas opções diferentes, dependendo dos requisitos do meu aplicativo. MAX_DURATION
permite especificar o número de minutos que a reconstrução do índice online ou a operação de troca de partição aguardará. Se o MAX_DURATION
valor for atingido, podemos definir o que acontece a seguir com base na configuração de ABORT_AFTER_WAIT
, que pode ser um valor de NONE
, SELF
ou BLOCKERS
:NONE
significa que a operação de índice continuará tentando a operação.SELF
significa que se oMAX_DURATION
for atingido, a operação (a reconstrução do índice online ou a troca de partição) será cancelada.- Se
BLOCKERS
for usado, ele matará todas as transações que estão bloqueando a reconstrução do índice online ou a operação de troca de partição (não é uma opção, na minha opinião, a ser usada levemente).BLOCKERS
também requerALTER ANY CONNECTION
permissão para a solicitação que emite a reconstrução do índice online ou a operação de troca de partição.
Os exemplos de código a seguir demonstram diferentes variações de configuração.
Comportamento padrão anterior a 2014 (aguardar indefinidamente)
- Executar o seguinte resultará no comportamento que estamos acostumados a ver antes do SQL Server 2014 – e ainda pode ser o que você deseja ou espera para determinados cenários:
ALTER TABLE [AdventureWorksDW2012].[dbo].[FactInternetSales] SWITCH PARTITION 37 TO [AdventureWorksDW2012].[dbo].[staging_FactInternetSales] WITH (WAIT_AT_LOW_PRIORITY (MAX_DURATION = 0 MINUTES, ABORT_AFTER_WAIT = NONE));
Aguarde 1 minuto e cancele a operação DDL
- O exemplo a seguir aguarda 1 minuto se houver uma transação de bloqueio e obterá um "período de tempo limite de solicitação de bloqueio excedido" para o
SWITCH
operação se a duração máxima for atingida:ALTER TABLE [AdventureWorksDW2012].[dbo].[FactInternetSales] SWITCH PARTITION 37 TO [AdventureWorksDW2012].[dbo].[staging_FactInternetSales] WITH (WAIT_AT_LOW_PRIORITY (MAX_DURATION = 1 MINUTES, ABORT_AFTER_WAIT = SELF));
Espere 1 minuto e mate o(s) bloqueador(es)
- Este exemplo espera 1 minuto se houver uma transação de bloqueio e, em seguida, eliminará as transações de bloqueio (origem ou destino incluído), permitindo o
SWITCH
operação para ser concluída. ALTER TABLE [AdventureWorksDW2012].[dbo].[staging_FactInternetSales] SWITCH PARTITION 37 TO [AdventureWorksDW2012].[dbo].[FactInternetSales] WITH (WAIT_AT_LOW_PRIORITY (MAX_DURATION = 1 MINUTES, ABORT_AFTER_WAIT = BLOCKERS));
No meu exemplo de um
DELETE
dentro de uma transação não confirmada, não houve erro na minha janela do SQL Server Management Studio, pois eu não tinha uma instrução em execução ativa, mas tentar outra instrução nessa sessão retornou a seguinte mensagem de erro (já que minha sessão foi encerrada):Msg 233, Level 20, State 0, Line 3
Ocorreu um erro de nível de transporte ao enviar a solicitação ao servidor. (provedor:Provedor de Memória Compartilhada, erro:0 – Nenhum processo está na outra extremidade do canal.)
Mate o(s) Bloqueador(es) Imediatamente (Fonte ou Destino para SWITCH)
- O seguinte é um exemplo de matar o bloqueador imediatamente – e no meu exemplo a troca aconteceu em menos de um segundo e, de fato, a sessão que era o bloqueador foi morta:
ALTER TABLE [AdventureWorksDW2012].[dbo].[FactInternetSales] SWITCH PARTITION 37 TO [AdventureWorksDW2012].[dbo].[staging_FactInternetSales] WITH (WAIT_AT_LOW_PRIORITY (MAX_DURATION = 0 MINUTES, ABORT_AFTER_WAIT = BLOCKERS));
Um último aspecto positivo que eu queria destacar…
O log de erros do SQL Server fornece algumas auditorias padrão do uso de espera de bloqueio de baixa prioridade, incluindo informações sobre o
ABORT_AFTER_WAIT
operação em linha com as informações da vítima:Data 10/09/2013 13:37:15
Log SQL Server (Atual – 10/09/2013 12:03:00)
Fonte spid51
Mensagem
Processo ID 57 foi morto por uma instrução ABORT_AFTER_WAIT =BLOCKERS DDL em database_id =5, object_id =309576141.
E você também verá entradas separadas para a própria operação original. Por exemplo:
Uma instrução ALTER TABLE SWITCH foi executada no banco de dados 'AdventureWorksDW2012', tabela 'staging_FactInternetSales' pelo nome do host 'WIN-4T7S36VMSD9', ID do processo do host 1360 com a tabela de destino 'AdventureWorksDW2012.dbo.FactInternetSales' usando as opções WAIT_AT_LOW_PRIORITY com MAX_DURATION =1 e ABORT_AFTER_WAIT =1 e ABORT_AFTER_WAIT =1 BLOQUEADORES. O bloqueio de sessões de usuário será eliminado após a duração máxima do tempo de espera.
Esse tipo de registro é muito útil para fins de solução de problemas e auditoria e fico feliz em vê-lo.