J'écris le schéma d'une base de données bancaire simple. Voici les spécifications de base:
- La base de données stockera les transactions contre un utilisateur et une devise.
- Chaque utilisateur a un solde par devise. Chaque solde est donc simplement la somme de toutes les transactions effectuées avec un utilisateur et une devise donnés.
- Un solde ne peut être négatif.
L'application bancaire communiquera avec sa base de données exclusivement par le biais de procédures stockées.
Je m'attends à ce que cette base de données accepte des centaines de milliers de nouvelles transactions par jour, ainsi que des requêtes de solde d'un ordre de grandeur supérieur. Pour servir les soldes très rapidement, je dois les pré-agréger. Dans le même temps, je dois garantir qu'un solde ne contredit jamais l'historique de ses transactions.
Mes options sont:
Ayez une
balances
table séparée et effectuez l'une des opérations suivantes:Appliquez des transactions aux tables
transactions
etbalances
. Utilisez laTRANSACTION
logique dans la couche de procédures stockées pour vous assurer que les soldes et les transactions sont toujours synchronisés. (Soutenu par Jack .)Appliquer des transactions à la
transactions
table et avoir un déclencheur qui met à jour labalances
table pour moi avec le montant de la transaction.Appliquez des transactions à la
balances
table et créez un déclencheur qui ajoute une nouvelle entrée dans latransactions
table avec le montant de la transaction.
Je dois m'appuyer sur des approches basées sur la sécurité pour m'assurer qu'aucune modification ne peut être apportée en dehors des procédures stockées. Sinon, par exemple, certains processus pourraient directement insérer une transaction dans la
transactions
table et, dans ce schéma,1.3
le solde correspondant serait désynchronisé.Avoir une
balances
vue indexée qui agrège les transactions de manière appropriée. Les soldes sont garantis par le moteur de stockage pour rester synchronisés avec leurs transactions. Je n'ai donc pas besoin de recourir à des approches basées sur la sécurité pour le garantir. Par contre, je ne peux plus imposer que les balances soient non négatives car les vues - même les vues indexées - ne peuvent pas avoir deCHECK
contraintes. (Soutenu par Denny .)Ayez juste une
transactions
table mais avec une colonne supplémentaire pour stocker le solde en vigueur juste après l'exécution de la transaction. Ainsi, le dernier enregistrement de transaction pour un utilisateur et une devise contient également leur solde actuel. (Suggéré ci-dessous par Andrew ; variante proposée par garik .)
Lorsque j'ai abordé ce problème pour la première fois, j'ai lu ces deux discussions et choisi l'option 2
. Pour référence, vous pouvez voir une implémentation de base ici .
Avez-vous conçu ou géré une telle base de données avec un profil de charge élevé? Quelle a été votre solution à ce problème?
Pensez-vous que j'ai fait le bon choix de design? Y a-t-il quelque chose que je devrais garder à l'esprit?
Par exemple, je sais que les modifications de schéma dans la
transactions
table nécessiteront la reconstruction de labalances
vue. Même si j'archive des transactions pour garder la base de données petite (par exemple en les déplaçant ailleurs et en les remplaçant par des transactions récapitulatives), devoir reconstruire la vue de dizaines de millions de transactions à chaque mise à jour de schéma signifiera probablement beaucoup plus de temps mort par déploiement.Si la vue indexée est la voie à suivre, comment puis-je garantir qu'aucun solde n'est négatif?
Archivage des transactions:
Permettez-moi de développer un peu les transactions d'archivage et les "transactions récapitulatives" que j'ai mentionnées ci-dessus. Premièrement, un archivage régulier sera une nécessité dans un système à forte charge comme celui-ci. Je souhaite maintenir la cohérence entre les soldes et l'historique de leurs transactions tout en permettant de transférer les anciennes transactions ailleurs. Pour ce faire, je remplacerai chaque lot de transactions archivées par un récapitulatif de leurs montants par utilisateur et par devise.
Ainsi, par exemple, cette liste de transactions:
user_id currency_id amount is_summary
------------------------------------------------
3 1 10.60 0
3 1 -55.00 0
3 1 -12.12 0
est archivé et remplacé par ceci:
user_id currency_id amount is_summary
------------------------------------------------
3 1 -56.52 1
De cette manière, un solde avec des transactions archivées conserve un historique complet et cohérent des transactions.