Como gerenciar 3,1 bilhões de linhas de dados?

14

Atualmente, estou encarregado de implementar um esquema de armazenamento para uma quantidade relativamente grande de dados. Os dados serão acessados ​​principalmente para determinar um data pointvalor atual , mas também sou obrigado a acompanhar os últimos seis meses do histórico de tendências / análises de dados.

Um requisito recente foi adicionado para rastrear o valor min/ max/ sumda última hora.

NOTA: Idealmente, eu gostaria de considerar uma opção do MongoDB, mas preciso demonstrar que exaurei as opções do SQL-Server primeiro.

Os dados

A tabela a seguir representa a fonte de dados principal (consultada com mais frequência). A tabela terá aproximadamente cinco milhões de linhas. As alterações nos dados serão predominantemente UPDATEdeclarações com declarações muito ocasionais INSERTapós o carregamento inicial dos dados. Optei por agrupar os dados, dataPointIdcomo você sempre estará selecionando all values for a given data point.

// Simplified Table
CREATE TABLE [dbo].[DataPointValue](
    [dataPointId]  [int] NOT NULL,
    [valueId]      [int] NOT NULL,
    [timestamp]    [datetime] NOT NULL,
    [minimum]      [decimal](18, 0) NOT NULL,
    [hourMinimum]  [decimal](18, 0) NOT NULL,
    [current]      [decimal](18, 0) NOT NULL,
    [currentTrend] [decimal](18, 0) NOT NULL,
    [hourMaximum]  [decimal](18, 0) NOT NULL,
    [maximum]      [decimal](18, 0) NOT NULL

    CONSTRAINT [PK_MeterDataPointValue] PRIMARY KEY CLUSTERED ([dataPointId],[valueId])
)

A segunda tabela é notavelmente maior, com aproximadamente 3,1 bilhões de linhas (representando os últimos seis meses de dados). Dados com mais de seis meses serão limpos; caso contrário, estritamente INSERTdeclarações de dados (~ 200 linhas / s, 720.000 linhas / hora, 17 milhões de linhas / semana).

// Simplified Table
CREATE TABLE [dbo].[DataPointValueHistory](
    [dataPointId] [int]            NOT NULL,
    [valueId]     [int]            NOT NULL,
    [timestamp]   [datetime]       NOT NULL,
    [value]       [decimal](18, 0) NOT NULL,
    [delta]       [decimal](18, 0) NOT NULL

    CONSTRAINT [PK_MeterDataPointHistory] PRIMARY KEY CLUSTERED ([dataPointId], [valueId], [timestamp])

)

A expectativa é que essa tabela tenha o dobro de tamanho à medida que o número de valores de pontos de dados rastreados aumenta para 400 linhas / s (portanto, atingir ~ 10 bilhões não está fora de questão).

A (s) questão (ões) (sim, estou perguntando mais de uma ... elas estão intimamente relacionadas).

Atualmente, estou usando um banco de dados SQL Server 2008 R2 Standard Edition. Provavelmente, defenderei a atualização para o Enterprise Edition se conseguir o nível de desempenho desejado com partições de tabela (ou o MongoDB se não conseguir atingir os níveis de desempenho necessários com o SQL-Server). Gostaria da sua opinião sobre o seguinte:


1) Dado que preciso para calcular o min, maxe sumdurante a última hora (como em now - 60 minutes). Qual é a melhor abordagem para rastrear dados recentes:

  • Mantenha dados recentes na memória do serviço de dados. Escreva min / max / média calculada com cada atualização de dados.

  • Consulte o histórico recente da tabela de histórico (afeta a próxima pergunta?) Durante cada instrução UPDATE. A consulta acessaria os dados mais recentes para obter um valor de ponto de dados e só deveria ser digitalizada nos últimos milhões de registros?

  • Armazenar o histórico recente na própria linha DataPointValue para evitar a pesquisa na tabela de histórico? Talvez armazenado como uma string delimitada e processado dentro do processo UPDATE?

  • Outra opção que não considerei?


2) Pois DataPointValueHistory, as consultas contra a tabela de dados sempre serão feitas por dataPointIde um ou mais valueId's. Os dados consultados normalmente são do último dia, semana ou mês, mas podem ser dos seis meses completos em alguns casos.

No momento, estou gerando um conjunto de dados de amostra para experimentar se faz mais sentido agrupar por dataPointId / valueId / timeStamp ou timeStamp / dataPointId / valueId. Se alguém tiver experiência em lidar com uma tabela desse tamanho e estiver disposto a oferecer seu insight, isso será apreciado. Estou inclinado para a última opção para evitar a fragmentação do índice, mas o desempenho da consulta é crítico.

  • Grupo DataPointValueHistory por dataPointId -> valueId -> timeStamp

  • Cluster DataPointValueHistorypor timeStamp -> dataPointId -> valueId


3) Finalmente, como mencionado acima, acho que fará sentido particionar a DataPointValueHistorytabela. Todas as sugestões sobre como melhor particionar os dados do histórico serão muito bem-vindas.

  • Se agrupado primeiro por carimbo de data e hora, acho que os dados devem ser particionados por semana (total de 27 partições). A partição mais antiga seria removida após a semana 27.

  • Se agrupado primeiro pelo dataPointId, estou pensando que os dados devem ser particionados por algum módulo da identificação?

Como tenho uma experiência muito limitada com o particionamento de tabelas, sua experiência será apreciada.

Calgary Coder
fonte
Você excluiu a versão desta pergunta no StackOverflow?
Taryn
@ bluefeet - Sim, foi sinalizado como off-topic ... então eu excluí a pergunta SO e recriei aqui (eu provavelmente deveria ter esperado que ela fosse migrada).
Calgary Coder
Não tem problema, eu estava apenas garantindo que não tivéssemos perguntas cruzadas.
Taryn
Na Standard Edition, você ainda pode particionar os dados usando visualizações particionadas e várias tabelas base. Não tenho certeza se você considerou isso.
Jon Seigel
@ Jon - Sim, eu considerei as partições manuais da tabela (essa opção específica será baseada na existência ou não de uma licença Enterprise ... se sim, por que a função é minha).
Calgary Coder

Respostas: