21 Fevereiro 2019

Carregar diferentes partições na mesma tabela simultaneamente

Quando se carrega uma tabela, se quisermos correr vários processos em simultâneo a carregar essa mesma tabela, existe a possibilidade de reutilizar o mesmo processo com diferentes variáveis de entrada. Existem dois cenários possíveis, tabelas particionadas e não particionadas.

  • Se a tabela que estamos a carregar não estiver particionada não há problemas de carregamento pois são carregados / atualizados / apagados diferentes registos da tabela. Mesmo quando um processo retém um bloqueio na tabela, o outro processo fica à espera que esse bloqueio seja liberto. Como não existem alterações na definição da tabela, o processo que continua sem problemas.
  • Se a tabela a ser carregada for particionada e se estiver a ser usado um processo de troca de partições quando é feita a troca, a definição da tabela é alterada. Mesmo quando um primeiro processo termine e mesmo que qualquer bloqueio seja liberto, dado que a definição da tabela mudou, o outro processo não consegue prosseguir e retorna erro devido à alteração de definição da tabela.

Se estivermos a trabalhar, como neste último caso, com tabelas particionadas, podem ser implementados processos de carregamento de diferentes formas com um processo que recebe uma variável de entrada e que identifica as diferentes partições a carregar.

  • Uma forma de implementar é um processo sequencial em que as partições são carregadas à vez. Desta forma o processo de ETL (que normalmente corre durante a noite e em geral permite disponibilizar os dados atualizados em horário laboral diurno) pode atrasar bastante, dependendo da volumetria de dados a ser carregados.
  • Outra forma é implementar um processo em que são consultadas tabelas de sistema para perceber se a tabela a ser carregada está bloqueada por algum outro processo, e criar um sistema de espera, em que um processo só é iniciado após qualquer bloqueio na tabela seja liberto. Desta forma, não há problemas com alterações da definição das tabelas.

Implementando esta última opção, podemos simplesmente adicionar o script seguinte no processo de troca de partições antes de ser iniciada cada troca:

No script de troca de partições, antes de iniciar o processo, utilizando uma estrutura de repetição (while loop), vão sendo feitas consultas à tabela sys.dm_tran_locks para identificar o número de bloqueios na tabela. Apenas quando este for igual a zero é que o processo é iniciado. Enquanto o número de bloqueios for superior a zero, o processo espera 1 ms antes de tentar de novo.

A título de exemplo, todos os objetos necessários para testar este processo foram criados numa base de dados de teste. As tabelas para a troca de partições são criadas automaticamente no processo com base na tabela a ser carregada.

Utilizando a lógica explicada acima, um primeiro processo inicia a troca de partições e causa um bloqueio na tabela. Os outros processos vão tentar começar a troca de partições quando nenhum outro processo retiver um bloqueio na tabela.

A principal diferença é uma questão de tempo. Sem a espera forçada, o segundo processo vai obter a definição da tabela antes do bloqueio do primeiro processo, o que causa erros devido à alteração da definição da tabela. Já com a espera forçada, o segundo processo só vai ler a definição da tabela após o bloqueio ser libertado, logo já vai obter a definição alterada, permitindo que a troca de partições corra com sucesso.

 

Scripts utilizados para criar a base de dados de teste e os objetos necessários

BI4ALL_DW_CREATE_DATABASE

BI4ALL_DW_ADD_FILEGROUPS

BI4ALL_DW_CREATE_SCHEMAS

CREATE admin.PARTITION_MAPPING

BI4ALL_DW_CREATE_USP_CREATE_PARTITION_TABLES

BI4ALL_DW_CREATE_USP_SWITCH_PARTITION

BI4ALL_DW_CREATE_PF_FCT_PARTITIONED

BI4ALL_DW_CREATE_PS_FCT_PARTITIONED

BI4ALL_DW_CREATE_TABLE_FCT_PARTITIONED

INSERT DUMMY DATA P1

INSERT DUMMY DATA P2

INSERT DUMMY DATA P3

 

Ana Santana
Consultant