Qual desses designs de tabela é melhor para o desempenho?

16

Me pediram para criar algo que rastreie o custo diário a ser cobrado nas contas e estou tentando descobrir um esquema de tabela de banco de dados que suporte isso.

Aqui está o que eu sei

  • A empresa possui mais de 2,5 milhões de contas
  • Destes, eles trabalham atualmente em média 200.000 por mês (que muda com os níveis de pessoal, que atualmente são baixos)
  • Eles têm 13 tipos de custo diferentes que gostariam de acompanhar e alertaram que podem adicionar mais no futuro
  • Eles querem que os custos sejam rastreados diariamente
  • Os custos não são divididos em todo o inventário. Eles são divididos pelo número de contas trabalhadas por mês (200.000), ou os usuários podem inserir identificadores de conta para aplicar um custo a um grupo de contas ou podem simplesmente especificar em quais contas aplicar o custo.

Meu primeiro pensamento foi um banco de dados normalizado:

AccountId
Encontro
CostTypeId
Montante

Meu problema com isso é: faça as contas. Essa tabela ficará enorme rapidamente. Supondo que todos os 13 tipos de custo sejam aplicados a todas as contas trabalhadas do mês atual, isso 200k * 13 * N days in monthé algo em torno de 75 a 80 milhões de registros por mês ou quase um bilhão de registros por ano.

Meu segundo pensamento foi desnormalizar um pouco

AccountId
Encontro
Custo total
CostType1
CostType2
CostType3
CostType4
CostType5
CostType6
CostType7
CostType8
CostType9
CostType10
CostType11
CostType12
CostType13

Esse método é mais desnormalizado e pode criar até 6 milhões de registros por mês ( 200k * N days in month), ou cerca de 72 milhões por ano. É muito menor que o primeiro método, no entanto, se a empresa decidir sobre um novo Tipo de Custo no futuro, outra coluna do banco de dados precisará ser adicionada.

Dos dois métodos, qual você prefere? Por quê? Existe outra alternativa que você possa imaginar que lidaria melhor com isso?

Estou mais interessado em relatar o desempenho, tanto no verão quanto nos detalhados. O trabalho que distribuirá os custos pelas contas será executado todas as noites quando não houver ninguém por perto. Uma preocupação secundária é o tamanho do banco de dados. O banco de dados existente já tem quase 300 GB e acredito que o espaço em disco seja de cerca de 500 GB.

O banco de dados é SQL Server 2005

Rachel
fonte
Então pegue outro disco. Discos são baratos. Você pode ter 2 TB pelo custo de uma reunião para discutir sobre isso.

Respostas:

9

Um bilhão de registros por ano não é muito.

Com o particionamento (por Costtype, talvez) e o arquivamento, é gerenciável.

O número de itens de dados a serem armazenados ainda é 200k * 13 * N. Como colunas, você obterá menos linhas por página e ocupará mais espaço do que como linhas. Você pode ganhar se "CostType1" não for um tipo de dados de comprimento fixo, mas for marginal.

"KISS" como se costuma dizer

gbn
fonte
3
@ Rachel, eu recomendaria definitivamente implementar um esquema de particionamento com um conjunto de dados tão grande. Se eles estão focando no trabalho e nos relatórios mês a mês, é melhor escolher uma chave de partição que possa coincidir com essa mentalidade. Além disso, se você configurar corretamente sua partição, poderá alternar e sair com facilidade dos dados da tabela para tabelas intermediárias, o que faz com que grandes cargas e exclusões de dados para conjuntos de dados em rolagem sejam instantâneas em segundos, em vez de horas.
David
6

Embora seu design possa certamente fazer a diferença de noite ou dia, nesse caso, eu me concentraria mais em índices, incluindo a cobertura de índices conforme necessário. Também examinaria algumas das ferramentas que o SQL Server fornece para lidar com tabelas muito grandes, como particionamento de tabelas.

Pense dessa maneira, mesmo que haja 80 bilhões de registros na tabela, com indexação adequada, aqueles em que você realmente se interessa em um determinado momento serão agrupados fisicamente em disco. Devido à maneira como os dados são organizados no SQL Server, os dados divididos por limites de índice também podem estar em outra tabela, pois não precisam ler a tabela inteira para obter o que precisam.

Se você também optar por particionar a tabela, poderá melhorar o tempo de acesso e inserir o tempo.


fonte
4

Eu normalizaria. Realizamos a contabilidade de custos da lucratividade da conta do cliente em um banco e geramos mais de 250 milhões de linhas de custos individuais usando centenas de drivers alocados por centro de custo ou contabilidade, ou por várias outras técnicas em milhões de contas todos os meses.

Por exemplo, o custo total de atendimento aos caixas eletrônicos foi dividido entre as contas que usaram caixas eletrônicos com base na quantidade relativa de uso. Portanto, se US $ 1 milhão foi gasto em serviços de caixas eletrônicos e apenas 5 clientes o usaram uma vez cada e um cliente o utilizou 5 vezes, esse cliente custou ao banco US $ 0,5 milhão e os outros clientes custaram US $ 0,1 milhão ao banco. Outros drivers podem ser muito mais complexos.

Por fim, você provavelmente achará escasso - certas contas não obtêm custos de determinadas fontes / drivers - e algumas contas não obtêm nada. Em um modelo normalizado, essas linhas não existem. No modelo desnormalizado, a linha existe, com algumas colunas vazias. Além disso, em um modelo normalizado esparso, você deve ver o desempenho melhorar, porque a existência de uma linha é geralmente mais rápida de verificar (com índice de cobertura no CostType) do que verificar todas as linhas com não NULL em um determinado "bucket" (mesmo com índices em cada coluna de valor - que você pode ver começa a ficar muito desperdiçada).

Cade Roux
fonte
SPARSE - Esse é um ponto muito bom que faz toda a diferença. Se for escasso, você economiza espaço normalizando. Caso contrário, não. Mas o espaço em disco é barato, então, pessoalmente, voto pela flexibilidade máxima (normalizada).
3

Independentemente do benefício de desempenho, eu definitivamente seria a favor da opção 1. A opção 2 estaria roubando Peter para pagar a Paul, na minha opinião.


fonte
2

Eu usaria a opção 1 e, se a velocidade do relatório se tornasse um problema no futuro, eu também adicionaria a tabela 2 e a preencheria em um banco de dados de relatórios em algum tipo de processo automatizado durante a noite / offpeak.

Você também pode considerar a agregação da estrutura diária da tabela 2 em acumulações adicionais semanais, mensais, trimestrais e anuais, se necessário.

Mas, como eu disse, também escolheria armazenar os dados 'brutos' na forma adequada (normalizada).

EJ Brennan
fonte
0

Considerando os volumes mencionados, eu optaria pela segunda opção, mas sem o TotalCost. Você poderia dizer que ainda está normalizado.


Editar: como alternativa, e dependendo dos seus requisitos e do tamanho do AccountId, você também pode considerar o seguinte:

AccountDate
-----------
AccountId  
Date  
AcDtID (surrogate key)

Costs
-------
AcDtID
CostTypeId  
Amount  

Com esse design, você ainda pode adicionar um TotalCost desnormalizado à primeira tabela e recalculá-lo todas as noites, permitindo executar alguns relatórios somente na primeira tabela.

Patrick Honorez
fonte
Eu tenho TotalCostlá porque a maioria dos relatórios é resumida e achei que seria mais rápido consultar um único valor do que adicionar 13 valores diferentes.
Provavelmente, mas você realmente introduz uma dependência transitiva. Esses registros serão sempre atualizados? ou apenas escrito e depois apenas ler?
Os registros serão atualizados sempre que um novo custo for aplicado a esse período. Após cerca de um mês, é improvável que o custo total seja atualizado, mas ainda é possível devido a fatores como taxas de suporte anual.
Então, cada atualização exigiria 2 atualizações e o campo TotalCost adiciona um risco de inconsistência.
Dependência transitiva, mas não necessariamente um risco de inconsistência - uma restrição CHECK () pode garantir que o TotalCost seja sempre a soma dos custos.
Mike Sherrill 'Cat Recall'
0

na verdade, você deve dividir a tabela de abas em duas tabelas para poder usar uma subconsulta e selecionar a segunda linha como uma coluna ou várias colunas. é mais flexível dessa maneira e com isso, você pode obter um resultado como o segundo mais facilmente.

Uğur Gümüşhan
fonte