Quale di questi design di tavoli è migliore per le prestazioni?


16

Mi è stato chiesto di creare qualcosa che tenga traccia dei costi giornalieri da riscuotere sugli account e sto cercando di capire uno schema di tabella del database che lo supporti.

Ecco quello che so

  • La società ha oltre 2,5 milioni di account
  • Di questi, attualmente lavorano in media 200.000 al mese (che cambia con i livelli di personale, che sono attualmente bassi)
  • Hanno 13 diversi tipi di costo che vorrebbero tenere traccia e hanno avvertito che potrebbero aggiungerne altri in futuro
  • Vogliono che i costi vengano tracciati quotidianamente
  • I costi non sono suddivisi in tutto l'inventario. Sono suddivisi tra il numero di account che vengono elaborati al mese (200.000) oppure gli utenti possono inserire identificatori di account per applicare un costo a un gruppo di account o semplicemente specificare a quali account applicare il costo.

Il mio primo pensiero è stato un database normalizzato:

Account ID
Data
CostTypeId
Quantità

Il mio problema con questo è, fare i conti. Questa tabella diventerà enorme rapidamente. Supponendo che tutti i 13 tipi di costo vengano applicati a tutti i conti lavorati per il mese corrente, ovvero 200k * 13 * N days in monthcirca 75-80 milioni di record al mese, o quasi un miliardo di record all'anno.

Il mio secondo pensiero era di denormalizzarlo un po '

Account ID
Data
Costo totale
CostType1
CostType2
CostType3
CostType4
CostType5
CostType6
CostType7
CostType8
CostType9
CostType10
CostType11
CostType12
CostType13

Questo metodo è più denormalizzato e può creare fino a 6 milioni di record al mese ( 200k * N days in month), o circa 72 milioni all'anno. È molto meno del primo metodo, tuttavia se la società decide in futuro un nuovo tipo di costo, sarà necessario aggiungere un'altra colonna del database.

Dei due metodi, quale preferisci? Perché? C'è un'altra alternativa a cui puoi pensare che gestirà meglio?

Sono più interessato a riportare le prestazioni, sia i rapporti estivi che quelli dettagliati. Il lavoro che distribuirà i costi sui conti verrà eseguito ogni notte quando non c'è nessuno in giro. Una preoccupazione secondaria è la dimensione del database. Il database esistente ha già quasi 300 GB e credo che lo spazio su disco sia di circa 500 GB.

Il database è SQL Server 2005


Quindi prendi un altro disco. I dischi sono economici. Puoi avere 2 TB per il costo di una riunione per discuterne.

Risposte:


9

Un miliardo di dischi all'anno non è molto.

Con il partizionamento (forse per Costtype) e l'archiviazione è gestibile.

Il numero di elementi di dati da memorizzare è ancora 200k * 13 * N. Come colonne, otterrai meno righe per pagina e occuperai più spazio rispetto alle righe. Potresti ottenere se "CostType1" non è un tipo di dati a lunghezza fissa, ma è marginale.

"BACIO" come si suol dire


3
@ Rachel Consiglio vivamente di implementare uno schema di partizionamento con un set di dati così grande. Se si stanno concentrando sul lavoro e sui rapporti di mese in mese, è meglio scegliere una chiave di partizione che possa coincidere con quella mentalità. Inoltre, se si configura correttamente la propria partizione, è possibile passare facilmente da e verso i dati dalla tabella alle tabelle di gestione temporanea, il che rende i carichi di dati di grandi dimensioni e le eliminazioni per i set di dati di rotazione uno schiocco che richiede secondi anziché ore.
David,

6

Mentre il tuo design può sicuramente fare la differenza di giorno o di notte, in questo caso mi concentrerei di più sugli indici, compresi gli indici di copertura, se necessario. Vorrei anche esaminare alcuni degli strumenti forniti da SQL Server per gestire tabelle molto grandi, come il partizionamento delle tabelle.

Pensala in questo modo, anche se nella tabella ci sono 80 miliardi di record, con una corretta indicizzazione, quelli che ti interessano in un dato momento verranno raggruppati fisicamente su disco. A causa del modo in cui i dati sono organizzati nel server SQL, i dati divisi per i limiti dell'indice possono anche trovarsi in un'altra tabella perché non devono leggere l'intera tabella per ottenere ciò di cui hanno bisogno.

Se si sceglie anche di partizionare la tabella, è possibile migliorare il tempo di accesso e il tempo di inserimento.


4

Vorrei normalizzare. Abbiamo effettuato la contabilità dei costi per la redditività dell'account cliente presso una banca e abbiamo generato oltre 250 milioni di righe di costi individuali utilizzando centinaia di driver allocati per centro di costo o contabilità generale o con varie altre tecniche su milioni di account ogni mese.

Ad esempio, il costo totale dell'assistenza agli sportelli automatici è stato suddiviso tra i conti che avevano utilizzato gli sportelli automatici in base al relativo importo di utilizzo. Quindi, se 1 milione di dollari sono stati spesi al servizio degli sportelli automatici e solo 5 clienti l'hanno usato una volta per volta e un cliente lo ha utilizzato 5 volte, allora quel cliente costa $ 0,5 milioni alla banca e gli altri clienti costano $ 1 milione ciascuno. Altri driver potrebbero essere molto più complessi.

Alla fine, probabilmente scoprirai che è scarso - alcuni account non ricevono costi da determinate fonti / driver - e alcuni account non ottengono nulla. In un modello normalizzato, quelle righe non esistono. Nel modello denormalizzato, la riga esiste, con alcune colonne vuote. Inoltre, in un modello normalizzato rado, dovresti vedere migliorare le prestazioni, perché l'esistenza di una riga è in genere più veloce da controllare (con indice di copertura su CostType) rispetto al controllo di tutte le righe con non-NULL in un particolare "bucket" (anche con indici su ogni colonna importo - che puoi vedere inizia a diventare molto dispendioso).


SPARSE - Questo è un ottimo punto che fa la differenza. Se è scarso, risparmi spazio normalizzando. Altrimenti no. Ma lo spazio su disco è economico, quindi personalmente voto per la massima flessibilità (normalizzata).

3

A prescindere dal vantaggio in termini di prestazioni, sarei sicuramente a favore dell'opzione 1. L'opzione 2 deruberebbe Peter per pagare Paul, secondo me.


2

Andrei con l'opzione 1, e quindi se la velocità di segnalazione diventasse un problema lungo la strada aggiungerei anche la tabella 2 e la inserirò in un database di report in una sorta di processo automatizzato notturno / offpeak.

Potresti anche considerare di raggruppare la struttura giornaliera della tabella 2 in ulteriori rollup settimanali, mensili, trimestrali e annuali, se garantito.

Ma, come ho detto, sceglierei anche di archiviare i dati "grezzi" nella forma corretta (normalizzata).


0

Considerando i volumi che menzioni, sceglierei la seconda opzione, ma senza TotalCost. Si potrebbe dire che è ancora normalizzato.


Modifica: in alternativa, a seconda delle esigenze e delle dimensioni dell'ID account, è possibile considerare anche quanto segue:

AccountDate
-----------
AccountId  
Date  
AcDtID (surrogate key)

Costs
-------
AcDtID
CostTypeId  
Amount  

Con tale progetto, è ancora possibile aggiungere un TotalCost denormalizzato alla prima tabella e farlo ricalcolare ogni notte, consentendo di eseguire alcuni report solo sulla prima tabella.


Ci sono TotalCostperché la maggior parte dei rapporti è sintetizzata e ho pensato che sarebbe stato più veloce interrogare un singolo valore piuttosto che aggiungere 13 valori diversi.

Probabilmente, ma poi si introduce davvero una dipendenza transitiva. Quei record saranno mai aggiornati? o appena scritto e poi solo letto?

I record verranno aggiornati ogni volta che un nuovo costo viene applicato a quell'intervallo di date. Dopo circa un mese è improbabile che il costo totale venga aggiornato, ma è ancora possibile a causa di cose come le commissioni di supporto annuali.

Quindi ogni aggiornamento richiederebbe 2 aggiornamenti e il campo TotalCost aggiunge un rischio di incoerenza.

Dipendenza transitiva, ma non necessariamente un rischio di incoerenza: un vincolo CHECK () può garantire che TotalCost sia sempre la somma dei costi.
Mike Sherrill 'Cat Recall',

0

dovresti effettivamente dividere la prima tabella in due tabelle in modo da poter usare una sottoquery e selezionare la seconda riga come colonna o più colonne. è più flessibile in questo modo e in questo modo puoi ottenere un risultato come il secondo più facilmente.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.