Pergunta

Eu tenho duas tabelas, registros estão sendo continuamente inserida a essas tabelas de fonte externa. Vamos dizer que essas tabelas estão mantendo estatísticas de interações do usuário. Quando um usuário é clicar em um botão os detalhes desse click (o usuário, hora do clique etc.) são gravados em uma das mesas. Quando um usuário mouseovers esse botão é adicionado um registo com detalhes para outra tabela.

Se houver muitos usuários constantemente interagindo com o sistema, haverá um monte de dados gerados, e essas tabelas vai crescer enormemente.

Quando eu quero olhar para os dados, eu quero vê-lo na resolução horária ou diária.

Existe uma maneira, ou melhor prática para continuamente resumir os dados de forma incremental (como os dados são coletados) na resolução exigiu?

Ou há uma melhor abordagem a este tipo de problema?

PS. O que eu encontrei até agora é ferramentas de ETL como Talend poderia tornar a vida fácil.

Update:. Estou usando o MySQL no momento, mas eu estou querendo saber as melhores práticas, independentemente da DB, meio ambiente etc

Foi útil?

Solução

A maneira normal de fazer isto em uma aplicação de baixa latência armazém de dados é ter uma tabela particionada com uma partição líder contendo algo que pode ser actualizado rapidamente (ou seja, sem ter de agregados Recalcular na mosca) mas com divisórias de arrasto backfilled com os agregados. Em outras palavras, a partição líder podem usar um esquema de armazenamento diferente para as partições de arrasto.

A maioria comercial e alguns open-source RDBMS plataformas (por exemplo, PostgreSQL) podem apoiar tais tabelas, que podem ser usados ??para fazer este tipo de coisa de uma forma ou de outra. Como você preencher o banco de dados de seus registros é deixado como um exercício para o leitor.

Basicamente, a estrutura deste tipo de sistema é assim:

  • Você tem uma tabela de partição em algum tipo de data ou valor de data e hora, Repartiu por hora, dia ou qualquer outra coisa grão parece apropriado. O registro entradas se anexado a esta tabela.

  • Como o tempo de janela desliza fora de um de partição, uma periódicas índices de trabalho ou resume e converte-lo em seu estado 'congelado'. Por exemplo, uma trabalho no Oracle pode criar bitmap índices em que partição ou uma actualização visão materializada para incluir resumo dados para essa partição.

  • Mais tarde, você pode soltar dados antigos, resumi-lo ou partições de mesclagem juntos.

  • Conforme o tempo passa, o trabalho periódica preenchimentos para trás do bordo de ataque partição. Os dados históricos é convertidos para um formato que empresta -se a performance estatística consulta, enquanto a borda frontal partição é mantido fácil de atualização rapidamente. Como esta partição não faz tem tanto de dados, consulta através de todo o conjunto de dados é relativamente rápido.

A natureza exata desse processo varia entre plataformas de SGBD.

Por exemplo, particionamento de tabela no SQL Server não é tão bom, mas isso pode ser feito com Analysis Services (um servidor OLAP que a Microsoft empacota com o SQL Server). Isso é feito por meio da configuração da partição de liderança como ROLAP puro (o servidor OLAP simplesmente emite uma consulta no banco de dados subjacente) e depois reconstruir as partições à direita como MOLAP (o servidor OLAP constrói suas próprias estruturas de dados especializadas, incluindo resumos persistentes conhecidos como 'agregações' ). serviços de análise pode fazer isso completamente transparente para o usuário. Ele pode reconstruir uma partição em segundo plano enquanto o velho ROLAP um ainda é visível para o usuário. Uma vez que a construção é terminado troca na partição; o cubo está disponível o tempo todo, sem interrupção do serviço para o usuário.

Oracle permite estruturas de partição para ser actualizado de forma independente, de modo índices pode ser construída, ou uma partição construído sobre uma visão materializada. Com Consulta re-escrita, o otimizador de consulta no Oracle pode trabalhar fora que valores agregados calculados a partir de uma tabela de fatos de base pode ser obtida a partir de uma visão materializada. A consulta irá ler os valores agregados a partir da visão em que partições estão disponíveis e a partir da partição de ponta onde eles não são materializadas.

PostgreSQL pode ser capaz de fazer algo semelhante, mas eu nunca olhei para implementar este tipo de sistema sobre ele.

Se você pode viver com interrupções periódicas, algo semelhante pode ser feito explicitamente, fazendo o sumarização ea criação de uma vista sobre os dados esquerda e à direita. Isso permite que este tipo de análise a ser feita em um sistema que não suporta o particionamento de forma transparente. No entanto, o sistema terá uma interrupção transitória como a vista é reconstruído, para que você não poderia realmente fazer isso durante o horário comercial -. A maioria das vezes seria overnight

Editar: Dependendo do formato dos arquivos de log ou o que as opções de registo estão disponíveis para você, existem várias maneiras de carregar os dados no sistema. Algumas opções são:

  • Escrever um script usando sua linguagem de programação favorita que lê os dados, analisa-os bits relevantes e inserts-lo no banco de dados. Isso poderia funcionar com bastante frequência, mas você tem que ter alguma forma de manter o controle de onde você está no arquivo. Tenha cuidado de bloqueio, especialmente no Windows. semântica bloqueio de arquivo padrão em Unix / Linux permitem que você faça isso (isso é como funciona tail -f), mas o comportamento padrão no Windows é diferente; ambos os sistemas teriam de ser escrito para jogar bem uns com os outros.

  • Em um sistema Unix-oid você poderia escrever seus logs para um tubo e tem um processo semelhante ao que acima da leitura do tubo. Isso teria a menor latência de todos, mas falhas no leitor poderia bloquear a sua aplicação.

  • Escrever uma interface de registro para seu aplicativo que diretamente preenche o banco de dados, ao invés de escrever a arquivos de log.

  • Use a carga API granel para o banco de dados (a maioria, se não todos têm este tipo de API disponível) e carregar os dados de registro em lotes. Escreva um programa semelhante ao da primeira opção, mas usar a API em massa-carga. Este mas usaria menos recursos do que preenchê-lo linha por linha, mas tem mais sobrecarga para configurar as cargas a granel. Seria uma carga menos frequentes adequado (talvez por hora ou por dia) e colocaria menos pressão sobre todo o sistema.

Na maioria destes cenários, mantendo o controle de onde você esteve se torna um problema. Polling o arquivo de detectar alterações podem ser infeasibly caro, então você pode precisar configurar o logger para que ele funciona de uma forma que funciona muito bem com o seu leitor de log.

  • Uma opção seria mudar o logger para que ele começa a escrever para um arquivo diferente a cada período (digamos, a cada poucos minutos). Ter o seu leitor de log começar periodicamente e carregar novos arquivos que não tenha já processados. Leia os arquivos antigos. Para que isso funcione, o esquema de nomeação para os arquivos devem ser com base no tempo de modo que o leitor sabe qual arquivo para pegar. Lidar com arquivos ainda em uso pelo aplicativo é mais trabalhosa (então você vai precisar para manter o controle de quanto tem sido lido), para que você gostaria de ler arquivos somente até o último período.

  • Outra opção é mover o arquivo, em seguida, lê-lo. Isso funciona melhor em sistemas de arquivos que se comportam como aqueles Unix, mas deve funcionar em NTFS. Você mover o arquivo, em seguida, lê-lo em lazer. No entanto, ele exige que o logger para abrir o arquivo em criar / anexar modo, escrever para ele e depois fechá-lo - não mantê-lo aberto e bloqueado. Este é definitivamente o comportamento Unix - a operação de movimentação tem que ser atômica. No Windows, você pode realmente ter que ficar sobre o logger para fazer este trabalho.

Outras dicas

Dê uma olhada RRDTool . É um banco de dados de round robin. Você definir as métricas que você deseja capturar, mas também pode definir a resolução que você armazená-lo a.

Por exemplo, você pode especificar a hora las, você mantém todos os segundos valor da informação; durante os últimos 24 horas - a cada minuto; para a semana passada, a cada hora, etc.

É amplamente utilizado para reunir estatísticas em sistemas como gânglios e Cactos .

Quando se trata de Divisão de dados e agregação (por hora ou outra coisa), o esquema em estrela (Kimball estrela) é uma solução bastante simples, mas poderosa. Suponha que, para cada clique que armazenamos tempo (para a segunda resolução), informações do usuário, o botão ID, e localização do usuário. Para facilitar o fatiamento, vou começar com tabelas de pesquisa pré-carregado para propriedades de objetos que raramente mudam - chamados tabelas de dimensões no mundo DW.
pagevisit2_model_02
A tabela dimDate tem uma linha para cada dia, com o número de atributos (campos) que descrevem um dia específico. A tabela pode ser pré-carregado por anos de antecedência, e deve ser atualizado uma vez por dia, se ele contém campos como DaysAgo, WeeksAgo, MonthsAgo, YearsAgo; caso contrário, pode ser “carregar e esquecer”. O dimDate permite a fácil corte por atributos data como

WHERE [YEAR] = 2009 AND DayOfWeek = 'Sunday'

Durante dez anos de dados a tabela tem apenas ~ 3650 linhas.

A tabela dimGeography é pré-carregado com regiões geografia de interesse - número de linhas depende de “resolução geográfica” requeridas nos relatórios, que permite a divisão de dados como

WHERE Continent = 'South America'

Uma vez carregado, ele raramente é alterado.

Para cada botão do site, há uma linha na tabela de dimButton, assim que uma consulta pode ter

WHERE PageURL = 'http://…/somepage.php'

A tabela dimUser tem uma linha por utilizador registado, este deve ser carregado com uma nova informação do usuário assim que o usuário se registra, ou pelo menos a nova informação do usuário deve estar na mesa antes de qualquer outra transação do usuário é gravado em tabelas de fatos.

Para cliques botão de gravação, eu vou adicionar a tabela factClick.
pagevisit2_model_01
A tabela factClick tem uma linha para cada clique de um botão de um usuário específico em um ponto no tempo. I ter usado TimeStamp (segunda resolução), e ButtonKey UserKey numa chave principal composto para a cliques de filtro-mais rapidamente do que um por segundo, a partir de um utilizador específico. Observe o campo Hour, que contém a parte hora do TimeStamp, um número inteiro na faixa de 0-23 para permitir a fácil corte por hora, como

WHERE [HOUR] BETWEEN 7 AND 9

Então, agora temos de considerar:

  • Como carregar a mesa? Periodicamente - talvez a cada hora ou a cada poucos minutos - do weblog usando uma ferramenta de ETL, ou uma solução de baixa latência usando algum tipo de processo de streaming de evento.
  • Quanto tempo para manter a informação na tabela?

Independentemente de saber se a tabela mantém informações para um dia ou apenas para alguns anos - deve ser dividida; ConcernedOfTunbridgeW explicou particionamento em sua resposta, então eu vou ignorá-lo aqui.

Agora, alguns exemplo de fatiamento por diferentes atributos (incluindo dia e hora)

Para simplificar as consultas, vou acrescentar uma visão para achatar o modelo:

/* To simplify queries flatten the model */ 
CREATE VIEW vClicks 
AS 
SELECT * 
FROM factClick AS f 
JOIN dimDate AS d ON d.DateKey = f.DateKey 
JOIN dimButton AS b ON b.ButtonKey = f.ButtonKey 
JOIN dimUser AS u ON u.UserKey = f.UserKey 
JOIN dimGeography AS g ON g.GeographyKey = f.GeographyKey

Um exemplo de consulta

/* 
Count number of times specific users clicked any button  
today between 7 and 9 AM (7:00 - 9:59)
*/ 
SELECT  [Email] 
       ,COUNT(*) AS [Counter] 
FROM    vClicks 
WHERE   [DaysAgo] = 0 
        AND [Hour] BETWEEN 7 AND 9 
        AND [Email] IN ('dude45@somemail.com', 'bob46@bobmail.com') 
GROUP BY [Email] 
ORDER BY [Email]

Suponha que eu estou interessado em dados para User = ALL. O dimUser é uma grande mesa, então eu vou fazer uma vista sem ele, para acelerar as consultas.

/* 
Because dimUser can be large table it is good 
to have a view without it, to speed-up queries 
when user info is not required 
*/ 
CREATE VIEW vClicksNoUsr 
AS 
SELECT * 
FROM factClick AS f 
JOIN dimDate AS d ON d.DateKey = f.DateKey 
JOIN dimButton AS b ON b.ButtonKey = f.ButtonKey 
JOIN dimGeography AS g ON g.GeographyKey = f.GeographyKey

Um exemplo de consulta

/* 
Count number of times a button was clicked on a specific page 
today and yesterday, for each hour. 
*/ 
SELECT  [FullDate] 
       ,[Hour] 
       ,COUNT(*) AS [Counter] 
FROM    vClicksNoUsr 
WHERE   [DaysAgo] IN ( 0, 1 ) 
        AND PageURL = 'http://...MyPage' 
GROUP BY [FullDate], [Hour] 
ORDER BY [FullDate] DESC, [Hour] DESC


Suponha que, para agregações não precisamos de manter informações de usuário específico, mas estão interessados ??apenas em data, hora, botão e geografia. Cada linha na tabela de factClickAgg tem um contador para cada hora um botão específico foi clicado a partir de uma área geográfica específica.
pagevisit2_model_03

A tabela factClickAgg pode ser carregado a cada hora, ou até mesmo no final de cada dia - dependendo dos requisitos para relatórios e analítica. Por exemplo, vamos dizer que a tabela é carregada no final de cada dia (após meia-noite), eu posso usar algo como:

/* At the end of each day (after midnight) aggregate data. */ 
INSERT  INTO factClickAgg 
        SELECT  DateKey 
               ,[Hour] 
               ,ButtonKey 
               ,GeographyKey 
               ,COUNT(*) AS [ClickCount] 
        FROM    vClicksNoUsr 
        WHERE   [DaysAgo] = 1 
        GROUP BY DateKey 
               ,[Hour] 
               ,ButtonKey 
               ,GeographyKey

Para simplificar as consultas, vou criar uma visão para achatar o modelo:

/* To simplify queries for aggregated data */ 
CREATE VIEW vClicksAggregate 
AS 
SELECT * 
FROM factClickAgg AS f 
JOIN dimDate AS d ON d.DateKey = f.DateKey 
JOIN dimButton AS b ON b.ButtonKey = f.ButtonKey 
JOIN dimGeography AS g ON g.GeographyKey = f.GeographyKey

dados Agora posso consulta agregados, por exemplo, por dia:

/* 
Number of times a specific buttons was clicked 
in year 2009, by day 
*/ 
SELECT  FullDate 
       ,SUM(ClickCount) AS [Counter] 
FROM    vClicksAggregate 
WHERE   ButtonName = 'MyBtn_1' 
        AND [Year] = 2009 
GROUP BY FullDate 
ORDER BY FullDate

Ou com mais algumas opções

/* 
Number of times specific buttons were clicked 
in year 2008, on Saturdays, between 9:00 and 11:59 AM 
by users from Africa 
*/ 

SELECT  SUM(ClickCount) AS [Counter] 
FROM    vClicksAggregate 
WHERE   [Year] = 2008 
        AND [DayOfWeek] = 'Saturday' 
        AND [Hour] BETWEEN 9 AND 11 
        AND Continent = 'Africa' 
        AND ButtonName IN ( 'MyBtn_1', 'MyBtn_2', 'MyBtn_3' )

Você poderia usar um db histórico como PI ou historiador. Aqueles pode ser mais dinheiro do que você quer gastar para este projeto, então você pode querer olhar para cima uma das alternativas de freeware, como o Realtime e História Banco de Dados pacote.

Quick 'n sujas sugestões.

[Supondo que você não pode alterar as tabelas subjacentes, que essas tabelas já gravar foram adicionadas as linhas de data / hora e que você tem permissão para criar objetos no DB].

  1. Criar uma vista (ou um par de pontos de vista), que tem um campo lógico sobre ele, o que gera um 'slot-número' única cortando-se a data nas tabelas. Algo como:

CREATE VIEW visão AS SELECT a, b, c, SUBSTR (date_field, x, y) Slot_Number A PARTIR DE TABELA;

O exemplo acima é simplificado, você provavelmente vai querer adicionar mais elementos a partir da data + tempo.

[por exemplo, data de dizer é '2010-01-01 10: 20: 23.111', você talvez pudesse gerar a chave como '2010-01-01 10:00': assim que sua resolução é de uma hora] <. / p>

  1. Opcionalmente: usar a exibição para gerar uma tabela real, como:

    CREATE TABLE frozen_data COMO * SELECT FROM VISTA ONDE Slot_Number = 'xxx;

Por que se preocupar com a etapa 1? Você realmente não tem que:. Apenas usando uma exibição pode tornar as coisas um pouco mais fáceis (do ponto de vista SQL)

Por que se preocupar com o passo 2? Apenas uma maneira de um (possivelmente) reduzindo a carga sobre as mesas já ocupadas: se você pode gerar dinamicamente DDL então você poderia produzir tabelas separadas com cópias dos 'slots' de dados:., Que você pode então trabalhar com

ou você pode criar um grupo de tabelas: uma por hora do dia. Criar um gatilho para preencher as tabelas secundárias:. A lógica do gatilho poderia segregrate qual tabela é escrito para

Em uma base diária, você teria que repor essas tabelas: a menos que você pode gerar tabelas em seu gatilho em seu DB. [Improvável Acho].

A sugestão de que não foi dada (até agora) pode ser usar CouchDB ou conceitos de banco de dados semelhantes que lidam com dados não estruturados.

Aguarde! Antes de saltar em mim no horror, deixe-me terminar.

CouchDB recolhe dados não estruturados (JSON & c); citando a visão geral técnica do website,

Para fazer face a este problema de adicionar estrutura de volta para desestruturado e dados de semi-estruturado, CouchDB integra um modelo de vista. As vistas são o método de agregar e relatórios sobre os documentos em um banco de dados, e são construído sob demanda para agregar, juntar e informar sobre os documentos do banco de dados. Visualizações são construídos de forma dinâmica e não afetam o documento subjacente, você pode ter como muitos diferentes representações vista dos mesmos dados como você gosta.

Veja definições são estritamente virtual e só exibir os documentos de banco de dados atual instância, tomada -los separados dos dados que exibição e compatível com replicação. visualizações do CouchDB são definidos dentro de documentos de design especial e pode replicar em frente do banco de dados Casos como documentos regulares, assim que não apenas repetições de dados em CouchDB, mas aplicativo inteiro projetos replicar também.

De suas necessidades, eu posso dizer que você precisa

  • para lotes de coleta de dados de forma confiável
  • a prioridade está na velocidade / confiabilidade, e não na estruturação de dados assim que entrar no sistema, nem na manutenção / verificação das propriedades estruturais do que você recolher (mesmo se você perder 1ms de dados do usuário pode não ser um tal grande problema)
  • você precisa de dados estruturado quando se trata a do DB

Pessoalmente, eu faria algo como:

  • dados coletados de cache no cliente (s) e salvá-lo em rajadas para couchdb
  • dependendo da carga de trabalho, manter um aglomerado de db (novamente, couchdb foi concebido para que) em sincronismo entre si
  • cada intervalo de ter um servidor gerar uma visão das coisas que você precisa (ou seja, a cada hora, etc), enquanto o outro (s) manter a coleta de dados
  • Salvar como (agora estruturado) vistas em uma base de dados adequada para a manipulação e jogando com ferramentas SQL, ou o que quer

último ponto é apenas um exemplo. Eu não tenho idéia o que você pretende fazer com ele.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top