Tarefa
Arquive todos, exceto um período de 13 meses consecutivos, de um grupo de tabelas grandes. Os dados arquivados devem ser armazenados em outro banco de dados.
- O banco de dados está no modo de recuperação simples
- As tabelas são de 50 mil linhas a vários bilhões e, em alguns casos, ocupam centenas de GB cada.
- As tabelas atualmente não estão particionadas
- Cada tabela possui um índice em cluster em uma coluna de data sempre crescente
- Cada tabela possui adicionalmente um índice não agrupado em cluster
- Todas as alterações de dados nas tabelas são inserções
- O objetivo é minimizar o tempo de inatividade do banco de dados primário.
- O servidor é 2008 R2 Enterprise
A tabela "archive" terá cerca de 1,1 bilhão de linhas, a tabela "live", cerca de 400 milhões. Obviamente, a tabela de arquivamento aumentará com o tempo, mas espero que a tabela ao vivo aumente razoavelmente rapidamente também. Diga 50% nos próximos dois anos, pelo menos.
Eu tinha pensado nos bancos de dados de expansão do Azure, mas infelizmente estamos no 2008 R2 e provavelmente permaneceremos lá por um tempo.
Plano atual
- Crie um novo banco de dados
- Crie novas tabelas particionadas por mês (usando a data modificada) no novo banco de dados.
- Mova os últimos 12 a 13 meses de dados para as tabelas particionadas.
- Faça uma troca de renomeação dos dois bancos de dados
- Exclua os dados movidos do banco de dados "arquivar" agora.
- Particione cada uma das tabelas no banco de dados "arquivar".
- Use trocas de partição para arquivar os dados no futuro.
- Percebo que terei que trocar os dados a serem arquivados, copiar essa tabela no banco de dados de arquivamento e depois trocá-lo na tabela de arquivamento. Isso é aceitável.
Problema: estou tentando mover os dados para as tabelas particionadas iniciais (na verdade, ainda estou fazendo uma prova de conceito). Estou tentando usar o TF 610 (conforme o Guia de desempenho de carregamento de dados ) e umINSERT...SELECT
instrução para mover os dados inicialmente pensando que seriam minimamente registrados. Infelizmente, toda vez que eu tento está totalmente logado.
Neste ponto, estou pensando que minha melhor aposta pode ser mover os dados usando um pacote SSIS. Estou tentando evitar isso, já que estou trabalhando com 200 tabelas e qualquer coisa que eu possa fazer por script, posso gerar e executar facilmente.
Há algo que esteja faltando em meu plano geral e o SSIS é minha melhor aposta para mover os dados rapidamente e com o uso mínimo do log (preocupações com espaço)?
Código de demonstração sem dados
-- Existing structure
USE [Audit]
GO
CREATE TABLE [dbo].[AuditTable](
[Col1] [bigint] NULL,
[Col2] [int] NULL,
[Col3] [int] NULL,
[Col4] [int] NULL,
[Col5] [int] NULL,
[Col6] [money] NULL,
[Modified] [datetime] NULL,
[ModifiedBy] [varchar](50) NULL,
[ModifiedType] [char](1) NULL
);
-- ~1.4 bill rows, ~20% in the last year
CREATE CLUSTERED INDEX [AuditTable_Modified] ON [dbo].[AuditTable]
( [Modified] ASC )
GO
-- New DB & Code
USE Audit_New
GO
CREATE PARTITION FUNCTION ThirteenMonthPartFunction (datetime)
AS RANGE RIGHT FOR VALUES ('20150701', '20150801', '20150901', '20151001', '20151101', '20151201',
'20160101', '20160201', '20160301', '20160401', '20160501', '20160601',
'20160701')
CREATE PARTITION SCHEME ThirteenMonthPartScheme AS PARTITION ThirteenMonthPartFunction
ALL TO ( [PRIMARY] );
CREATE TABLE [dbo].[AuditTable](
[Col1] [bigint] NULL,
[Col2] [int] NULL,
[Col3] [int] NULL,
[Col4] [int] NULL,
[Col5] [int] NULL,
[Col6] [money] NULL,
[Modified] [datetime] NULL,
[ModifiedBy] [varchar](50) NULL,
[ModifiedType] [char](1) NULL
) ON ThirteenMonthPartScheme (Modified)
GO
CREATE CLUSTERED INDEX [AuditTable_Modified] ON [dbo].[AuditTable]
(
[Modified] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON ThirteenMonthPartScheme (Modified)
GO
CREATE NONCLUSTERED INDEX [AuditTable_Col1_Col2_Col3_Col4_Modified] ON [dbo].[AuditTable]
(
[Col1] ASC,
[Col2] ASC,
[Col3] ASC,
[Col4] ASC,
[Modified] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON ThirteenMonthPartScheme (Modified)
GO
Mover código
USE Audit_New
GO
DBCC TRACEON(610);
INSERT INTO AuditTable
SELECT * FROM Audit.dbo.AuditTable
WHERE Modified >= '6/1/2015'
ORDER BY Modified
fonte
Respostas:
Por que você não está obtendo log mínimo?
Eu achei o Guia de Desempenho de Carregamento de Dados , que você mencionou, um recurso extremamente valioso. No entanto, também não é 100% abrangente, e suspeito que a grade já seja suficientemente complexa para que o autor não tenha adicionado uma coluna
Table Partitioning
para quebrar as diferenças de comportamento, dependendo se a tabela que recebe as inserções está particionada. Como veremos mais adiante, o fato de a tabela já estar particionada parece inibir o registro mínimo.Abordagem recomendada
Com base nas recomendações do Guia de desempenho de carregamento de dados (incluindo a seção "Carregamento em massa de uma tabela particionada"), bem como uma vasta experiência no carregamento de tabelas particionadas com dezenas de bilhões de linhas, eis a abordagem que eu recomendaria:
As diferenças quando comparadas à sua abordagem original:
TABLOCK
um mês de cada vez, usando a alternância de partições para colocar os dados na tabela particionada.DELETE
para limpar a tabela antiga será totalmente registrada. Talvez você possaTRUNCATE
ou solte a tabela e crie uma nova tabela de arquivamento.Comparação de abordagens para mover o ano recente de dados
Para comparar abordagens em uma quantidade razoável de tempo na minha máquina, usei um
100MM row
conjunto de dados de teste que eu gerava e que segue o seu esquema.Como você pode ver nos resultados abaixo, há um grande aumento no desempenho e redução nas gravações de log, carregando dados em um heap usando a
TABLOCK
dica. Há um benefício adicional se isso for feito uma partição por vez. Também vale a pena notar que o método de uma partição por vez pode ser facilmente paralelizado ainda mais se você executar várias partições ao mesmo tempo. Dependendo do seu hardware, isso pode gerar um bom impulso; normalmente carregamos pelo menos quatro partições ao mesmo tempo no hardware da classe de servidor.Aqui está o script de teste completo .
Notas finais
Todos esses resultados dependem do seu hardware até certo ponto. No entanto, meus testes foram conduzidos em um laptop quad-core padrão com unidade de disco giratório. É provável que as cargas de dados sejam muito mais rápidas se você estiver usando um servidor decente que não tenha muita outra carga no momento em que estiver conduzindo esse processo.
Por exemplo, executei a abordagem recomendada em um servidor de desenvolvimento real (Dell R720) e vi uma redução de
76 seconds
(de156 seconds
no meu laptop). Curiosamente, a abordagem original de inserção em uma tabela particionada não teve a mesma melhoria e ainda assumiu o controle12 minutes
no servidor de desenvolvimento. Presumivelmente, isso ocorre porque esse padrão gera um plano de execução serial e um único processador no meu laptop pode corresponder a um único processador no servidor de desenvolvimento.fonte
Este pode ser um bom candidato para Biml. Uma abordagem seria criar um modelo reutilizável que migrasse dados para uma única tabela em pequenos períodos com um contêiner For Each. O Biml percorreria sua coleção de tabelas para criar pacotes idênticos para cada tabela qualificada. Andy Leonard tem uma introdução em sua Stairway Series .
fonte
Talvez, em vez de criar o novo banco de dados, restaure o banco de dados real para um novo banco de dados e exclua os dados mais recentes de 12 a 13 meses. Em seu banco de dados real, exclua os dados que não estão contidos na área de arquivamento recém-criada. Se exclusões grandes forem um problema, talvez você possa excluir conjuntos de 10 K ou mais por meio de script para fazer isso.
Suas tarefas de particionamento não parecem interferir e parecem ser aplicáveis a qualquer banco de dados após suas exclusões.
fonte