Domanda

Ho due tabelle, i record vengono continuamente inseriti in queste tabelle da fonti esterne.Supponiamo che queste tabelle conservino le statistiche delle interazioni degli utenti.Quando un utente fa clic su un pulsante, i dettagli di quel clic (utente, ora del clic, ecc.) vengono scritti in una delle tabelle.Quando un utente passa il mouse su quel pulsante, viene aggiunto un record con i dettagli ad un'altra tabella.

Se ci sono molti utenti che interagiscono costantemente con il sistema, verranno generati molti dati e tali tabelle cresceranno enormemente.

Quando voglio guardare i dati, voglio vederli con una risoluzione oraria o giornaliera.

Esiste un modo o una procedura ottimale per riepilogare continuamente i dati in modo incrementale (man mano che i dati vengono raccolti) nella risoluzione richiesta?

Oppure esiste un approccio migliore a questo tipo di problema?

PS.Ciò che ho scoperto finora è che strumenti ETL come Talend potrebbero semplificarmi la vita.

Aggiornamento:Al momento sto utilizzando MySQL, ma mi chiedo quali siano le migliori pratiche indipendentemente dal DB, dall'ambiente, ecc.

È stato utile?

Soluzione

Il modo normale per farlo su un'applicazione di data warehouse a bassa latenza è avere una tabella partizionata con una partizione iniziale contenente qualcosa che può essere aggiornato rapidamente (ad es.senza dover ricalcolare gli aggregati al volo) ma con partizioni finali riempite con gli aggregati.In altre parole, la partizione principale può utilizzare uno schema di archiviazione diverso rispetto alle partizioni finali.

La maggior parte delle piattaforme RDBMS commerciali e alcune open source (ad es.PostgreSQL) può supportare tabelle partizionate, che possono essere utilizzate per fare questo tipo di cose in un modo o nell'altro.Il modo in cui popoli il database dai tuoi log è lasciato come esercizio per il lettore.

Fondamentalmente, la struttura di questo tipo di sistema è la seguente:

  • Hai partito una tabella in una sorta di data o valore della data, partizionata per ora, giorno o qualunque grana sembri appropriato.Le voci del registro vengono aggiunte a questa tabella.

  • Mentre la finestra temporale scivola fuori da una partizione, un lavoro periodico indicizza o lo riassume e lo converte nel suo stato "congelato".Ad esempio, un lavoro su Oracle può creare indici BitMap su tale partizione o aggiornare una vista materializzata per includere i dati di riepilogo per quella partizione.

  • Più tardi, puoi abbandonare i vecchi dati, riassumerli o unire le partizioni insieme.

  • Col passare del tempo, il lavoro periodico si riempie dietro la partizione al limite.I dati storici vengono convertiti in un formato che si presta a query statistiche performanti mentre la partizione anteriore è mantenuta facile da aggiornare rapidamente.Poiché questa partizione non ha così tanti dati, la query su tutto il set di dati è relativamente veloce.

La natura esatta di questo processo varia tra le piattaforme DBMS.

Ad esempio, il partizionamento delle tabelle su SQL Server non è poi così buono, ma può essere fatto con Analysis Services (un server OLAP che Microsoft fornisce in bundle con SQL Server).Ciò viene fatto configurando la partizione principale come ROLAP puro (il server OLAP emette semplicemente una query sul database sottostante) e quindi ricostruendo le partizioni finali come MOLAP (il server OLAP costruisce le proprie strutture dati specializzate inclusi riepiloghi persistenti noti come "aggregazioni" ).I servizi di analisi possono farlo in modo completamente trasparente per l'utente.Può ricostruire una partizione in background mentre quella vecchia ROLAP è ancora visibile all'utente.Una volta terminata la compilazione, viene scambiata la partizione;il cubo è sempre disponibile senza interruzione del servizio all'utente.

Oracle consente l'aggiornamento indipendente delle strutture delle partizioni, in modo da poter costruire indici o creare una partizione su una vista materializzata.Con la riscrittura delle query, l'ottimizzatore delle query in Oracle può capire che le cifre aggregate calcolate da una tabella dei fatti di base possono essere ottenute da una vista materializzata.La query leggerà le cifre aggregate dalla vista materializzata dove le partizioni sono disponibili e dalla partizione all'avanguardia dove non lo sono.

PostgreSQL potrebbe essere in grado di fare qualcosa di simile, ma non ho mai pensato di implementare questo tipo di sistema su di esso.

Se riesci a convivere con interruzioni periodiche, qualcosa di simile può essere fatto esplicitamente eseguendo il riepilogo e impostando una visualizzazione sui dati iniziali e finali.Ciò consente di eseguire questo tipo di analisi su un sistema che non supporta il partizionamento in modo trasparente.Tuttavia, il sistema subirà un'interruzione temporanea man mano che la visualizzazione viene ricostruita, quindi non è possibile eseguire questa operazione durante l'orario lavorativo, nella maggior parte dei casi avviene durante la notte.

Modificare: A seconda del formato dei file di registro o delle opzioni di registrazione disponibili, esistono vari modi per caricare i dati nel sistema.Alcune opzioni sono:

  • Scrivi uno script utilizzando il tuo linguaggio di programmazione preferito che legga i dati, analizzi i bit rilevanti e li inserisca nel database.Questo potrebbe essere eseguito abbastanza spesso ma devi avere un modo per tenere traccia di dove ti trovi nel file.Fare attenzione al blocco, soprattutto su Windows.La semantica predefinita di blocco dei file su Unix/Linux ti consente di farlo (ecco come tail -f funziona) ma il comportamento predefinito su Windows è diverso;entrambi i sistemi dovrebbero essere scritti per funzionare bene l'uno con l'altro.

  • Su un sistema unix-oid potresti scrivere i tuoi log su una pipe e avere un processo simile a quello sopra letto leggendo dalla pipe.Ciò avrebbe la latenza più bassa di tutte, ma eventuali errori nel lettore potrebbero bloccare l'applicazione.

  • Scrivi un'interfaccia di registrazione per la tua applicazione che popola direttamente il database, anziché scrivere file di registro.

  • Utilizzare l'API di caricamento in blocco per il database (la maggior parte, se non tutti, hanno questo tipo di API disponibile) e caricare i dati di registrazione in batch.Scrivi un programma simile alla prima opzione, ma utilizza l'API di caricamento di massa.Questo utilizzerebbe meno risorse rispetto al popolamento riga per riga, ma comporta un sovraccarico maggiore per impostare i carichi di massa.Sarebbe opportuno un carico meno frequente (magari orario o giornaliero) e sottoporrebbe meno sforzo al sistema nel suo complesso.

Nella maggior parte di questi scenari, tenere traccia di dove sei stato diventa un problema.Il polling del file per individuare le modifiche potrebbe essere incredibilmente costoso, quindi potrebbe essere necessario impostare il logger in modo che funzioni in modo che funzioni bene con il tuo lettore di log.

  • Un'opzione potrebbe essere quella di modificare il logger in modo che inizi a scrivere su un file diverso ogni periodo (diciamo ogni pochi minuti).Chiedi al tuo lettore di registro di avviarsi periodicamente e caricare nuovi file che non ha già elaborato.Leggi i vecchi file.Perché funzioni, lo schema di denominazione dei file dovrebbe essere basato sull'ora in modo che il lettore sappia quale file prendere.Gestire i file ancora in uso dall'applicazione è più complicato (dovrai quindi tenere traccia di quanto è stato letto), quindi vorresti leggere i file solo fino all'ultimo periodo.

  • Un'altra opzione è spostare il file e poi leggerlo.Funziona meglio su filesystem che si comportano come quelli Unix, ma dovrebbe funzionare su NTFS.Sposti il ​​file e poi lo leggi a tuo piacimento.Tuttavia, è necessario che il logger apra il file in modalità creazione/aggiunta, vi scriva e quindi lo chiuda, non che lo tenga aperto e bloccato.Questo è sicuramente un comportamento Unix: l'operazione di spostamento deve essere atomica.Su Windows potrebbe essere necessario stare sopra il logger per farlo funzionare.

Altri suggerimenti

Date un'occhiata a RRDTool . Si tratta di un database di round robin. Si definiscono i parametri che si desidera catturare, ma può anche definire la risoluzione che si memorizza a.

Ad esempio, è possibile specificare per l'las ora, si tiene ogni secondi di informazioni; per le ultime 24 ore - ogni minuto; per la settimana passata, ogni ora, ecc.

E 'ampiamente utilizzato per raccogliere le statistiche in sistemi come gangli e Cactus .

Quando si tratta di affettare e aggregazione dei dati (per tempo o qualcos'altro), lo schema a stella (Kimball stella) è una soluzione abbastanza semplice, ma potente. Supponiamo che per ogni clic memorizziamo il tempo (a seconda risoluzione), informazioni dell'utente, l'ID pulsante e la posizione dell'utente. Per consentire una facile affettare e sminuzzare, inizierò con tabelle di ricerca pre-caricati per le proprietà di oggetti che raramente cambiano - cosiddette tabelle di dimensioni nel mondo DW.
pagevisit2_model_02
La tabella dimDate ha una riga per ogni giorno, con il numero di attributi (campi) che descrivono un giorno specifico. La tabella può essere pre-caricato per anni di anticipo, e dovrebbe essere aggiornato una volta al giorno se contiene campi come DaysAgo, WeeksAgo, MonthsAgo, YearsAgo; altrimenti si può essere “carico e dimenticare”. Il dimDate permette una facile affettare per attributi data come

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

Per dieci anni di dati della tabella ha solo ~ 3650 righe.

La tabella dimGeography è precaricato con le regioni geografia di interesse - il numero di righe dipende da “risoluzione geografica” richieste nel rapporto, permette per affettare i dati come

WHERE Continent = 'South America'

Una volta caricato, è raramente cambiata.

Per ciascun pulsante del sito, non esiste una riga nella tabella dimButton, quindi una query può avere

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

La tabella dimUser ha una riga per utente registrato, questo dovrebbe essere caricato con un nuovo informazioni utente non appena l'utente si registra, o almeno le nuove informazioni utente dovrebbe essere nella tabella prima di qualsiasi altra operazione dell'utente viene registrato fatto tabelle.

Per registrare i clic sul pulsante, aggiungerò la tavola factClick.
pagevisit2_model_01
La tabella factClick ha una riga per ogni clic di un pulsante da un utente specifico in un punto nel tempo. Ho usato TimeStamp (seconda risoluzione), ButtonKey e UserKey in una chiave primaria composta scatti per filtrare-out veloce di una al secondo da un utente specifico. Nota il campo Hour, contiene la parte ora del TimeStamp, un numero intero nella gamma 0-23 per consentire un facile affettare all'ora, come

WHERE [HOUR] BETWEEN 7 AND 9

Quindi, ora dobbiamo prendere in considerazione:

  • Come caricare la tabella? Periodicamente - forse ogni ora o ogni pochi minuti - dal weblog utilizzando uno strumento di ETL, o una soluzione a bassa latenza utilizzando qualche tipo di processo-streaming dell'evento.
  • Per quanto tempo di mantenere le informazioni nella tabella?

Indipendentemente dal fatto che la tavola mantiene le informazioni per un solo giorno o per qualche anno - dovrebbe essere partizionato; ConcernedOfTunbridgeW ha spiegato il partizionamento nella sua risposta, quindi salterò qui.

Ora, alcuni esempi di affettare e sminuzzare per diversi attributi (inclusi giorno e ora)

Per semplificare le query, aggiungerò al fine di appiattire il modello:

/* 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

Un esempio di query

/* 
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]

Supponiamo che io sia interessato a dati per User = ALL. Il dimUser è un grande tavolo, quindi farò una vista senza di essa, per velocizzare le query.

/* 
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

Un esempio di query

/* 
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


Supponiamo che per aggregazioni non abbiamo bisogno di tenere informazioni utente specifico, ma interessa solo la data, l'ora, il pulsante e la geografia. Ogni riga della tabella factClickAgg ha un contatore per ogni ora un pulsante specifico stato cliccato da una determinata area geografica.
pagevisit2_model_03

La tabella factClickAgg può essere caricato ogni ora, o anche alla fine di ogni giornata - a seconda delle esigenze di reporting e analitica. Per esempio, diciamo che la tabella viene caricata alla fine di ogni giorno (dopo la mezzanotte), posso usare qualcosa come:

/* 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

Per semplificare le query, creerò fine di appiattire il modello:

/* 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

Ora posso interrogare i dati aggregati, ad esempio per giorno:

/* 
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

o con un paio di opzioni

/* 
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' )

È possibile usare un db storica come PI o Historian. Coloro che potrebbe essere più denaro di quanto si vuole spendere per questo progetto, così si potrebbe desiderare di cercare una delle alternative del freeware, come il in tempo reale e Storia pacchetto database .

Ricerca veloce 'n sporchi suggerimenti.

[Supponendo non è possibile modificare le tabelle sottostanti, che tali tabelle già registrano sono state aggiunte le righe ora / data e che si ha l'autorizzazione per creare oggetti nel DB].

  1. creare una vista (o di un paio di punti di vista), che ha un campo logico su di esso, che genera un unico 'numero-slot' tagliando la data nelle tabelle. Qualcosa di simile:

CREATE VIEW Vedi come SELECT a, b, c, SUBSTR (date_field, x, y) Slot_Number     A PARTIRE DAL           TABELLA;

L'esempio precedente è semplificato, probabilmente avrete bisogno di aggiungere in più elementi dalla data + tempo.

[ad esempio, dire data è '2010-01-01 10: 20: 23.111', si potrebbe forse generare la chiave come '2010-01-01 10:00': così la risoluzione è di un'ora].

  1. A scelta: usare la vista per generare un tavolo reale, come:

    CREATE TABLE frozen_data     COME     SELECT * FROM VISTA     DOVE     Slot_Number = 'xxx;

Perché perdere tempo con la fase 1? In realtà non è necessario:. Solo utilizzando una vista potrebbe rendere le cose un po 'più facili (da un punto di vista SQL)

Perché perdere tempo con la fase 2? Solo un modo di un (forse) riducendo il carico sui tavoli già occupati: se è possibile generare dinamicamente DDL allora si potrebbe produrre tabelle separate con le copie delle 'slot' di dati:. Che è quindi possibile lavorare con

O si potrebbe istituire un gruppo di tabelle: una per ora del giorno. Creare un trigger per popolare le tabelle secondarie:. La logica del trigger potrebbe segregrate quale tabella viene scritto

Su base giornaliera si dovrebbe resettare queste tabelle: se non è possibile generare tabelle del trigger sul vostro DB. [Improbabile Credo].

Un suggerimento che non è stato dato (finora) potrebbe essere quella di utilizzare CouchDB Concetti di database simili o che si occupano di dati non strutturati.

Aspetta! Prima di saltare su di me con orrore, lasciami finire.

CouchDB raccoglie i dati non strutturati (JSON & C); citando il quadro tecnico dal sito web,

  

Per risolvere questo problema di aggiunta   la struttura di nuovo a non strutturati e   dati semi-strutturati, couchdb   integra un modello di vista. Le viste sono il   modalità di aggregazione e reporting su   i documenti in un database, e sono   built on-demand per aggregare, unire e   riferire sui documenti di database. Visualizzazioni   sono costruiti in modo dinamico e non incidono   il documento di base, si può avere   come molti altri vista rappresentazioni   dei dati stessi come ti piace.

     

Visualizza le definizioni sono rigorosamente virtuali   e visualizzare solo i documenti   l'istanza del database corrente, rendendo   li separano dai dati che   display e compatibile con   replica. viste CouchDB sono definiti   all'interno di documenti design particolare e   in grado di replicare in tutta banca dati   casi come documenti regolari, in modo da   che non solo i dati replicati in   CouchDB, ma intera applicazione   disegni replicano anche.

Da vostre esigenze, vi posso dire è necessario

  • per raccogliere un sacco di dati in modo affidabile
  • la priorità è sulla velocità / affidabilità, non sulla strutturazione dei dati appena entrare nel sistema né sul mantenimento / verifica delle proprietà strutturali di ciò si raccolgono (anche se si perde 1ms di dati utente potrebbe non essere tale grosso problema)
  • avete bisogno di dati strutturati, quando si tratta del DB

Personalmente, mi piacerebbe fare qualcosa di simile:

  • cache di dati sul client (s) raccolti e salvarlo a raffica su CouchDB
  • a seconda del carico di lavoro, di tenere un gruppo di db (ancora una volta, CouchDB è stato progettato per questo) in sincronia tra loro
  • ogni intervallo dispone di un server di generare una vista delle cose che avete bisogno (vale a dire ogni ora, ecc), mentre l'altro (s) mantenere la raccolta dei dati
  • salvare tali opinioni (ora strutturati) in un database adeguato per la manipolazione e giocare con gli strumenti di SQL, o qualsiasi altra cosa

Ultimo punto è solo un esempio. Non ho idea di che cosa avete intenzione di fare con esso.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top