Domanda

Abbiamo un obbligo di memorizzare 500 misure al secondo, provenienti da diversi dispositivi. Ogni misura è costituito da un timestamp, un tipo di quantità e diversi valori vettoriali. In questo momento v'è di 8 valori di vettore per la misurazione, e possiamo considerare questo numero per essere costante per le esigenze del nostro progetto prototipo. Stiamo utilizzando HNibernate. I test sono fatti in SQLite (file di disco db, non in memoria), ma la produzione sarà probabilmente MsSQL.

La nostra classe di entità di misura è quella che detiene una singola misurazione, e si presenta come questo:

public class Measurement
{
    public virtual Guid Id { get; private set; }
    public virtual Device Device { get; private set; }
    public virtual Timestamp Timestamp { get; private set; }
    public virtual IList<VectorValue> Vectors { get; private set; }
}

I valori vettoriali sono memorizzati in una tabella separata, in modo che ciascuno di essi fa riferimento la sua misurazione genitore attraverso una chiave esterna.

Abbiamo fatto un paio di cose per garantire che SQL generato è (abbastanza) efficace: stiamo usando Guid.Comb per la generazione di ID, stiamo Flushing circa 500 elementi in una singola transazione, ADO.Net dimensione del lotto è impostato su 100 (credo SQLite non supporta gli aggiornamenti in batch? ma potrebbe essere utile in seguito).

Il problema

In questo momento possiamo inserire 150-200 misurazioni al secondo (che non è abbastanza veloce, anche se questo è SQLite stiamo parlando). Guardando il codice SQL generato, possiamo vedere che in una singola transazione inseriamo (come previsto):

  • 1 timestamp
  • 1 di misura
  • 8 valori vettoriali

Il che significa che stiamo facendo 10x più singoli inserti di tabella:. 1500-2000 al secondo

Se abbiamo messo tutto (tutti gli 8 valori vettoriali e il timestamp) nella tabella misurazione (aggiunta di 9 colonne dedicate), sembra che potremmo aumentare la nostra velocità inserto fino a 10 volte.

Il passaggio a SQL Server migliora le prestazioni, ma vorremmo sapere se ci potrebbe essere un modo per evitare costi inutili di performance correlati al modo in banca dati è organizzata in questo momento.

[Edit]

Con in memoria SQLite ricevo circa 350 oggetti / sec (3500 singoli inserti di tabella), che credo sia quanto di buono come si arriva con NHibernate (prendendo questo post per riferimento: http://ayende.com/Blog/archive/2009/08/22/nhibernate-perf -tricks.aspx ).

Ma potrei anche passare a SQL server e fermata assumendo le cose, giusto? Voglio aggiornare il mio post non appena ho provarlo.

[Aggiornamento]

mi sono trasferito a SQL server e appiattito la mia gerarchia, ho provato memorizzando 3000 misurazioni / sec per diverse ore e sembra funzionare bene.

È stato utile?

Soluzione

Personalmente, direi andare per esso:. Denormalizzare, e quindi creare un processo ETL per portare questi dati in un formato più normalizzato per l'analisi utilizzo / regolare

In sostanza la situazione ideale per voi potrebbe essere quello di avere un database separato (o tabelle anche solo separati nello stesso database, se necessario), che tratta l'acquisizione dei dati come una questione del tutto separata da averlo nel formato in cui si necessità di elaborarlo.

Questo non vuol dire che avete bisogno di buttare via le entità che hai creato intorno alla vostra struttura del database corrente:. Solo che si dovrebbe anche creare quelle tabelle denormalizzati e fare un ETL per portarli in Si potrebbe utilizzare SSIS ( anche se è ancora abbastanza buggy e irritabile) per portare i dati nel vostro serie normalizzata di tabelle periodicamente, o anche un app C # o altro processo in massa di caricamento.

EDIT: Ciò presuppone, naturalmente, che la tua analisi non ha bisogno di essere fatto in tempo reale: solo la raccolta dei dati. Molto spesso, le persone non hanno bisogno (e, a volte, sarebbe in realtà preferiscono non avere) l'aggiornamento in tempo reale dei dati di analisi. E 'una di quelle cose che suoni bene sulla carta, ma in pratica è inutile.

Se alcune persone che analizzano questi dati richiedono l'accesso in tempo reale, si potrebbe costruire un set di strumenti contro la "bare metal" denormalizzato dati transazionali se lo si desidera: ma abbastanza frequentemente quando si scava veramente in requisiti, le persone che svolgono analisi non è necessario genuina in tempo reale (e, in alcuni casi, che preferirebbe avere un insieme più statico di dati su cui lavorare!): e in tal caso, l'ETL periodica avrebbe funzionato abbastanza bene. Devi solo stare insieme con i tuoi utenti target e scoprire che cosa hanno veramente bisogno.

Altri suggerimenti

Bene, sarebbe dipendono. Sono le 8 valori vettoriali un numero duro e veloce che non cambierà mai? Poi denormalizing nel tuo caso potrebbe avere un senso (ma solo test sul reale hardware e database in uso dirà). Se si potesse 9 misure la prossima settimana, non farlo.

Direi che è necessario passare prima al server SQL e l'attrezzatura che verrà eseguita sulla prima di cercare di decidere cosa fare.

Una volta che avete acceso run profiler. E 'del tutto possibile che NHibernate non sta creando il codice SQL più performante per il vostro inserto.

Il fatto che si dispone di un insieme di vettori che probabilmente sono in fase di spaccatura sull'inserto può essere parte del problema di prestazioni. Potrebbe essere meglio avere 8 variabili separati piuttosto che un insieme che deve essere suddivisa.

Si sta parlando di oltre 40 milioni di dischi al giorno, questo sta per richiedere alcuni importanti hardware e un database molto ben progettato. E 'anche possibile che un database relazionale non è la scelta migliore per questo (non ho idea di come si desidera utilizzare questa quantità di dati). Quanto tempo si stanno tenendo questi dati, la dimensione qui sta per sfuggire di mano molto molto rapidamente.

E 'possibile bulkinsert i record in un gruppo una volta al minuto, invece? inserimento di massa è più veloce di gran lunga rispetto fila da inserti di riga.

Il vostro disegno deve prendere in considerazione come si utilizzano i dati così come inserirla. Generalmente le cose accelerare inserti possono rallentare seleziona e viceversa. Potrebbe essere necessario un data warehouse che viene caricato una volta al giorno per l'analisi (e una query veloce per essere in grado di mostrare la cruda fino al secondo di dati).

In primo luogo, mossa per il database di destinazione; prestazioni in base SqlLite potrebbe non essere indicativi dell'andamento basato su MsSql

In secondo luogo, la misura in cui il collo di bottiglia è; estemporaneo Mi permetto che è il disco e un database in memoria si comporta molto meglio.

Poi denormalizzare se necessario, con un processo ETL come suggerito in precedenza.

Elaborazione evento-stream ha un modo di dire: " se si colpisce il disco, sei morto "; -)

Hai pensato di usare SqlBulkCopy? Funziona veramente veloce. L'ho usato in un ambiente di produzione e raggiunto 10.000+ inserti su una singola tabella meno di un secondo, con una macchina server SQL 2005. Hai solo bisogno di preparare DataTable (s) per essere massa inserito nella vostra applicazione. Ecco un esempio.

        public static void SQLBulkCopyInsert(DataTable dtInsertRows, string destinationTableName, string[] columnMappings)
    {
        using (SqlBulkCopy sbc = new SqlBulkCopy(DBHelper.Secim2009DB.ConnectionString, SqlBulkCopyOptions.UseInternalTransaction))
        {                
            sbc.DestinationTableName = destinationTableName;
            // Number of records to be processed in one go
            sbc.BatchSize = 30000;
            // Map the Source Column from DataTabel to the Destination Columns in SQL Server 2005 Person Table

            foreach (string columnMapping in columnMappings)
            {
                sbc.ColumnMappings.Add(columnMapping, columnMapping);
            }

            // Number of records after which client has to be notified about its status
            sbc.NotifyAfter = dtInsertRows.Rows.Count;
            // Event that gets fired when NotifyAfter number of records are processed.
            sbc.SqlRowsCopied += new SqlRowsCopiedEventHandler(sbc_SqlRowsCopied);
            // Finally write to server
            sbc.WriteToServer(dtInsertRows);
            sbc.Close();
        }
    }

    public static void sbc_SqlRowsCopied(object sender, SqlRowsCopiedEventArgs e)
    {            

    }

Do non solo denormalizzare. Progettare per i risultati, utilizzando un modello di progettazione utile. A volte, un modello di progettazione utile per performance dà un design diverso da quello che si ottiene seguendo le regole di normalizzazione.

Non credo che la situazione sta per essere aiutato da denormalizing. Quasi tutte le persone che sostengono denormalizing dire che il miglioramento delle prestazioni non vengono quando si archiviano i nuovi dati. Vengono quando si recuperano i dati. Si dovrà capire come che si applica al vostro caso.

posso dirvi questo. Se si finisce per immagazzinare attraverso molteplici processi concorrenti, il vostro disegno si tradurrà in colli di bottiglia gravi, e potrebbe ben gestito più lento di un disegno normalizzato.

Ma non prendere la mia parola. Sperimentare. Analizzare. Imparare. Prosper.

"Abbiamo un obbligo di memorizzare 500 misure al secondo, provenienti da diversi dispositivi."

Non utilizzare DBMS di immagazzinare questo tipo di dati.

Quali sono i motivi di persone utilizzano DBMS?

(a) Possono far rispettare i vincoli per voi sui dati che si sta tentando di registrare. Ma non si ha alcuna. I dati misurazioni sono quello che sono e hanno bisogno di essere accettati. Non ci sono vincoli.

(b) Possono garantire la coerenza e l'integrità dei dati aziendali preziosi nel caso di violazioni (1) di vincolo e (2) gravi guasti di sistema come disco I / O errors. Ma dal momento che non hanno vincoli, non si applica (1). E per quanto riguarda (2), cosa vorresti fare con le vostre misure se un disco di I / O impedisce di errore che venga registrato? Le vostre misure sono persi, non importa quale.

Quindi IMO, non avete alcuna ragione che cosa così mai usare un DBMS. Dump il vostro carico di misure in un file flat e processo che, se necessario.

Si potrebbe prendere in considerazione altre alternative di database. MSSQL fornisce un sacco di funzionalità, ma che aggiunge un certo overhead.

Una risorsa eccellente per l'elaborazione ad alte prestazioni (come quello che si sta tentando di fare) è a http://highscalability.com/

Uno dei casi di studio che hanno avuto è la memorizzazione di migliaia di statistiche del dispositivo in un database. La soluzione è stata più database MySQL e richiesta percorso una base all'ID dispositivo. Nel complesso - il sito può fornire eccellenti casi di studio. Può essere che si può trovare una possibile soluzione c'è.

Timur

Utilizzare i DBMS giusti e hardware. Test su un'altra piattaforma con hardware diverso vi dirà nulla di prestazioni.

Denormalizzazione è improbabile che le prestazioni aiutare a scrivere in quanto, per definizione, significa che si sta creando i dati ridondanti e quindi si sarebbe fare più lavoro per ogni processo di scrittura, non di meno.

Le cifre che hai citato non sono eccezionali per lo streaming scenari di dati e perfettamente realizzabile utilizzando l'hardware giusto, ma credo che NHibernate sta per essere un importante fattore limitante per voi. Penso che sia improbabile nHib è una scelta sensata per questo genere di cose.

Hai pensato di usare alcune delle tecnologie che offrono il suport speciale per lo streaming di fonti di dati e CEP? Per esempio:. OSISoft PI, Microsoft StreamInsight e funzionalità FILESTREAM di SQL Server

Bisogna chiedersi: "Perché abbiamo normalizzare?"

Ci sono tre ragioni principali:

  1. La consistenza dei dati
  2. Aggiorna Velocità
  3. Dimensioni

Coerenza dei dati

E 'bello avere elenchi a discesa e tutte le righe che significano la stessa cosa che ha lo stesso FK, giusto? Abbastanza ovvio. Questo è molto importante per DB con i dati più "redattori". Ma questo è solo buono come i nostri processi. Diciamo che si tratta di una banca dati di volo e c'è una voce per National Airport di Washington DC ... e un po 'aggiunge una nuova voce per Reagan National Airport di Washington DC ... l'FK la volontà di essere lì, ed essere utilizzato nella tabella dei bambini ma ha vinto 't essere vale molto ... Ma è comunque una buona cosa per farlo ...

Aggiornamento Velocità

Quello che abbiamo dovuto fare è aggiornare la riga per il National Airport con un nuovo nome. Perché c'è una sola riga padre, lo rende un cambiamento molto semplice. Se il mio tavolo di volo ha avuto il testo sarei stato aggiornando milioni di righe.

Dimensioni

Se l'ho fatto negozio "Reagan" su tutti i record, ci vorrebbero più spazio di un FK di dire, 19. Dimensioni usato per essere davvero un grosso problema, ma SAN rende abbastanza irrilevante.


Conclussions

Ok, quindi sei preoccupato che il vostro SOLO la raccolta dei dati applicazione non può mantenere i nomi degli strumenti rette? È la coerenza dei dati sarà una sfida?

Ok, quindi Quante volte pensi di modificare il nome dello strumento o dati punto? Voglio dire disciolto O2 è dissolto O2, torbidità è torbidità, giusto? Ma se avete fatto bisogno di fare un aggiornamento di massa Scommetto che avrai tempo di inattività tra le esecuzioni di farlo. Quindi questo non è un problema.

Ok, dimensioni Quindi, certo ... che è un sacco di misurazioni; ma, non fare la misura "Ossigeno disciolto", DO2 va bene ... quanto più grande è che rispetto ad alcuni FK come "7? Spendere lo spazio per risparmiare tempo.

Do non Normalizzare perché sei sempre stato detto che i progettisti di database buona cosa fanno. Sai perché lo stai facendo e perché si sceglie quello che si sta scegliendo.

Sì. Riterrei ridurre l'overhead degli inserti sia denormalizzazione (appiattimento dei dati) e chunking i dati dal tempo. Vorrei progettare il mio database in modo che ogni record memorizza un intero secondo valore di dati al dispositivo:

public class Measurement 
{ 
    public Guid ID { get; private set; } 
    public Device Device { get; private set; }
    public Sample[] { get; private set; }

    public DateTime FirstTimestamp { get; private set; } 
    public DateTime LastTimestamp { get; private set; } 
} 

public class Sample
{ 
    public DateTime Timestamp { get; private set; } 
    public VectorValue[] Vectors { get; private set; } 
}

Ci sono vari modi di memorizzazione di tipi complessi (come ad esempio una lista di liste, in questo caso) in un singolo record. colonne XML e CLR tipi definiti dall'utente , sono due esempi.

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