Sto scrivendo lo schema per un semplice database bancario. Ecco le specifiche di base:
- Il database memorizzerà le transazioni contro un utente e una valuta.
- Ogni utente ha un saldo per valuta, quindi ogni saldo è semplicemente la somma di tutte le transazioni rispetto a un determinato utente e valuta.
- Un saldo non può essere negativo.
L'applicazione bancaria comunicherà con il proprio database esclusivamente tramite procedure memorizzate.
Mi aspetto che questo database accetti centinaia di migliaia di nuove transazioni al giorno, oltre a bilanciare le query su un ordine di grandezza superiore. Per servire rapidamente i saldi, devo pre-aggregarli. Allo stesso tempo, devo garantire che un saldo non contraddica mai la sua cronologia delle transazioni.
Le mie opzioni sono:
Avere una
balances
tabella separata ed effettuare una delle seguenti operazioni:Applicare le operazioni sia alle
transactions
ebalances
tabelle. Usa laTRANSACTION
logica nel mio livello di procedura memorizzata per assicurarti che i saldi e le transazioni siano sempre sincronizzati. (Supportato da Jack .)Applicare le transazioni alla
transactions
tabella e disporre di un trigger che aggiorna labalances
tabella per me con l'importo della transazione.Applicare le transazioni alla
balances
tabella e disporre di un trigger che aggiunge una nuova voce nellatransactions
tabella con l'importo della transazione.
Devo fare affidamento su approcci basati sulla sicurezza per assicurarmi che non possano essere apportate modifiche al di fuori delle procedure memorizzate. In caso contrario, ad esempio, alcuni processi potrebbero inserire direttamente una transazione nella
transactions
tabella e, in base allo schema,1.3
il saldo pertinente non sarebbe sincronizzato.Avere una
balances
vista indicizzata che aggrega le transazioni in modo appropriato. I saldi sono garantiti dal motore di archiviazione per rimanere sincronizzati con le loro transazioni, quindi non ho bisogno di fare affidamento su approcci basati sulla sicurezza per garantirlo. D'altra parte, non posso più far valere i saldi in modo non negativo poiché le viste - anche le viste indicizzate - non possono avereCHECK
vincoli. (Supportato da Denny .)Avere solo una
transactions
tabella ma con una colonna aggiuntiva per archiviare il saldo effettivo subito dopo l'esecuzione della transazione. Pertanto, l'ultimo record di transazione per un utente e una valuta contiene anche il saldo corrente. (Suggerito di seguito da Andrew ; variante proposta da Garik .)
Quando ho affrontato per la prima volta questo problema, ho letto queste due discussioni e ho deciso l'opzione 2
. Per riferimento, puoi vedere un'implementazione semplice di questo qui .
Hai progettato o gestito un database come questo con un profilo di carico elevato? Qual è stata la tua soluzione a questo problema?
Pensi che ho fatto la scelta giusta per il design? C'è qualcosa che dovrei tenere a mente?
Ad esempio, so che le modifiche dello schema alla
transactions
tabella richiederanno la ricostruzione dellabalances
vista. Anche se sto archiviando le transazioni per mantenere piccolo il database (ad es. Spostandoli altrove e sostituendoli con transazioni di riepilogo), dover ricostruire la vista su decine di milioni di transazioni con ogni aggiornamento dello schema significherà probabilmente un tempo di inattività significativamente maggiore per distribuzione.Se la vista indicizzata è la strada da percorrere, come posso garantire che nessun saldo sia negativo?
Transazioni di archiviazione:
Consentitemi di approfondire un po 'l'archiviazione delle transazioni e le "transazioni di riepilogo" che ho menzionato sopra. Innanzitutto, l'archiviazione regolare sarà necessaria in un sistema ad alto carico come questo. Voglio mantenere la coerenza tra i saldi e la cronologia delle loro transazioni, consentendo nel contempo che le vecchie transazioni vengano spostate altrove. Per fare ciò sostituirò ogni lotto di transazioni archiviate con un riepilogo degli importi per utente e valuta.
Quindi, ad esempio, questo elenco di transazioni:
user_id currency_id amount is_summary
------------------------------------------------
3 1 10.60 0
3 1 -55.00 0
3 1 -12.12 0
viene archiviato e sostituito con questo:
user_id currency_id amount is_summary
------------------------------------------------
3 1 -56.52 1
In questo modo, un saldo con le transazioni archiviate mantiene una cronologia delle transazioni completa e coerente.