Revisione del progetto per problemi di memoria di fronte all'applicazione

4

Mi scuso in anticipo per la durata di questo post, ma voglio dipingere un'immagine precisa dei problemi che la mia app sta affrontando e quindi porrò alcune domande di seguito;

Sto cercando di risolvere alcuni problemi di progettazione auto-inflitti che stanno causando il blocco dell'applicazione a causa di errori di memoria insufficienti.

Una descrizione in forma abbreviata del dominio problematico è la seguente;

  • L'applicazione contiene un "set di dati" costituito da numerosi file di testo contenenti dati correlati
  • Un singolo file di testo all'interno del set di dati di solito contiene circa 20 "intestazioni" che contengono metadati relativi ai dati che contiene. Contiene inoltre una sezione delimitata da una grande scheda contenente dati correlati ai dati in uno degli altri file di testo contenuti nel set di dati. Il numero di colonne per file è molto variabile da 2 a 256+ colonne.

L'applicazione originale è stata scritta per consentire agli utenti di caricare un set di dati, mappare determinate colonne di ciascuno dei file che in pratica indicano le informazioni chiave sui file per mostrare come sono correlati e identificare alcuni nomi di colonna previsti. Una volta eseguita questa operazione, viene eseguito un processo di convalida per applicare le varie regole e garantire che tutte le relazioni tra i file siano valide. Fatto ciò, i dati vengono importati in un database SQL Server. La progettazione del database è un modello EAV (Entity-Attribute-Value) utilizzato per soddisfare le colonne variabili per file. So che l'EAV ha i suoi detrattori, ma in questo caso, ritengo che sia stata una scelta ragionevole dati i dati disparati e il numero variabile di colonne presentate in ciascun set di dati.

Il problema di memoria

Dato che la dimensione combinata di tutti i file di testo era al massimo di circa 5 mega, e nel tentativo di ridurre il tempo di transazione del database, si è deciso di leggere TUTTI i dati dai file in memoria e quindi eseguire quanto segue;

  • esegue tutta la convalida mentre i dati erano in memoria
  • si riferiscono a un modello di oggetto
  • Avvia la transazione DB e scrivi le colonne chiave riga per riga, prendendo nota dell'ID della riga scritta (tutte le tabelle nel database utilizzano le colonne Identity), quindi l'ID della riga appena scritta viene applicato a tutti i dati correlati
  • Una volta aggiornati tutti i dati correlati con le informazioni chiave a cui si riferisce, questi record vengono scritti utilizzando SqlBulkCopy. A causa del nostro modello EAV, abbiamo essenzialmente; x colonne di y righe da scrivere, dove x può essere maggiore di 256+ e le righe spesso sono decine di migliaia.
  • Una volta che tutti i dati sono stati scritti senza errori (possono richiedere alcuni minuti per set di dati di grandi dimensioni), eseguire il commit della transazione.

Il problema ora deriva dal fatto che ora stiamo ricevendo singoli file contenenti oltre 30 mega di dati. In un set di dati, possiamo ricevere qualsiasi numero di file. Abbiamo iniziato a vedere set di dati di circa 100 mega in arrivo e mi aspetto che diventerà più grande da qui in poi. Con file di queste dimensioni, i dati non possono nemmeno essere letti in memoria senza che l'app cada, figuriamoci essere convalidato e importato. Prevedo di dover modificare grossi pezzi del codice per consentire la convalida mediante l'analisi dei file riga per riga e non sono esattamente deciso su come gestire l'importazione e le transazioni.

Potenziali miglioramenti

  • Mi sono chiesto se utilizzare i GUID per mettere in relazione i dati anziché fare affidamento sui campi di identità. Ciò consentirebbe ai dati di essere correlati prima di scrivere nel database. Ciò certamente aumenterebbe lo spazio di archiviazione richiesto. Soprattutto in un design EAV. Pensereste che sia una cosa ragionevole da provare, o semplicemente persisto con i campi di identità (le chiavi naturali non possono essere considerate attendibili per essere uniche in tutti i submitter).
  • Uso di tabelle di staging per ottenere dati nel database e solo l'esecuzione della transazione per copiare i dati dall'area di staging alle tabelle di destinazione effettive.

Domande

  • Per sistemi come questo che importano grandi quantità di dati, come si fa a mantenere piccole le transazioni. Li ho mantenuti il più piccoli possibile nel progetto corrente, ma sono ancora attivi per diversi minuti e scrivono centinaia di migliaia di record in un'unica transazione. C'è una soluzione migliore?
  • La sezione dati delimitata da tabulazioni viene letta in un DataTable per essere visualizzato in una griglia. Non ho bisogno della piena funzionalità di un DataTable, quindi sospetto che sia eccessivo. Esiste comunque la possibilità di disattivare varie funzionalità di DataTable per renderle più leggere?

Ci sono altre cose ovvie che dovresti fare in questa situazione per ridurre al minimo l'impronta di memoria dell'applicazione descritta sopra?

Grazie per la cortese attenzione.

    
posta Mr Moose 16.11.2011 - 09:09
fonte

2 risposte

1

L'EAV è quasi sempre un errore (forse non nel tuo caso ma ....).

I dati in ogni colonna devono avere un significato (probabilmente contenuto nei meta dati dell'intestazione!).

Forse potresti tradurre le intestazioni dei meta-dati in una definizione di tabella "corretta" con colonne utilizzabili?

In entrambi i casi caricarei ogni set di dati direttamente in una tabella intermedia 1 riga per record. Anche se hai bisogno di nominare le colonne (col1, col2, col3 .... col256) o solo una colonna molto ampia. Quindi modifica il processo di convalida per utilizzare le tabelle intermedie anziché caricare tutto in memoria.

Se è possibile fare un riferimento incrociato tra i vari file, è necessario che abbiano già un significato chiave / identificatore completo. Potresti usarli come chiavi primarie piuttosto che lasciare che il database assegni una chiave, risparmiandoti così il lavoro di legare di nuovo tutto.

    
risposta data 16.11.2011 - 11:29
fonte
1

I GUID possono essere una buona idea per identificare univocamente un singolo record, specialmente nei casi di EAV o dove un record deve essere univoco tra schemi, database e ambienti. Possono effettivamente causare problemi di prestazioni in un tipico RDBMS, tuttavia il confronto di una stringa di caratteri con altre stringhe di caratteri è molto più costoso rispetto al confronto dei dati numerici. Questo potrebbe avere un impatto negativo sulle query, ma l'effetto potrebbe essere trascurabile o non applicabile per te. Quindi usa il tuo miglior giudizio.

Le tabelle di staging sono un'ottima idea se le applicazioni che dipendono da questi dati non hanno requisiti espliciti per i dati in tempo reale al 100%. È sempre possibile scaricare l'elaborazione pesante e l'utilizzo della memoria coinvolti nell'elaborazione di questi dati nelle tabelle principali durante le ore non di punta. Permettono anche di eseguire la convalida dei dati in un secondo momento.

    
risposta data 16.11.2011 - 15:08
fonte

Leggi altre domande sui tag