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.
Réponses:
Je ne suis pas familier avec la comptabilité, mais j'ai résolu certains problèmes similaires dans des environnements de type inventaire. Je stocke les totaux cumulés sur la même ligne que la transaction. J'utilise des contraintes pour que mes données ne soient jamais fausses, même en cas de simultanéité élevée. J'ai écrit la solution suivante à l'époque en 2009 :
Le calcul des totaux cumulés est notoirement lent, que vous le fassiez avec un curseur ou avec une jointure triangulaire. Il est très tentant de dénormaliser, de stocker les totaux cumulés dans une colonne, surtout si vous la sélectionnez fréquemment. Cependant, comme d'habitude lorsque vous dénormalisez, vous devez garantir l'intégrité de vos données dénormalisées. Heureusement, vous pouvez garantir l'intégrité des totaux cumulés avec des contraintes - tant que toutes vos contraintes sont approuvées, tous vos totaux cumulés sont corrects. De même, vous pouvez facilement vous assurer que le solde actuel (les totaux cumulés) n’est jamais négatif - l’application par d’autres méthodes peut également être très lente. Le script suivant illustre la technique.
la source
Ne pas autoriser les clients à avoir un solde inférieur à 0 est une règle de gestion (ce qui changerait rapidement étant donné que les frais pour des éléments tels que les traites supplémentaires sont le moyen par lequel les banques utilisent la plus grande partie de leur argent). Vous voudrez gérer cela dans le traitement de l'application lorsque des lignes sont insérées dans l'historique des transactions. Surtout que certains clients peuvent se retrouver avec une protection contre les découverts et se faire facturer des frais et ne pas permettre la saisie de montants négatifs.
Jusqu'ici, j'aime bien ce que vous faites avec cela, mais si c'est pour un projet réel (pas pour une école), il faut beaucoup de réflexion dans les règles de gestion, etc. Une fois que vous avez un système bancaire en place et en cours d'exécution, il n'y a pas beaucoup de place pour une nouvelle conception car il existe des lois très spécifiques concernant l'accès des personnes à leur argent.
la source
Une approche légèrement différente (similaire à votre deuxième option) à considérer consiste à ne prendre que la table de transaction, avec une définition de:
Vous voudrez peut-être également un identifiant de transaction / une commande afin de pouvoir traiter deux transactions avec la même date et améliorer votre requête de récupération.
Pour obtenir le solde actuel, tout ce dont vous avez besoin est le dernier enregistrement.
Méthodes pour obtenir le dernier enregistrement :
Les inconvénients:
Les transactions pour l'utilisateur / la devise doivent être sérialisées pour maintenir un solde exact.
Avantages:
Edit: Quelques exemples de requêtes sur la récupération du solde en cours et pour mettre en évidence les inconvénients (Merci @Jack Douglas)
la source
SELECT TOP (1) ... ORDER BY TransactionDate DESC
sera très difficile à mettre en œuvre de telle manière que SQL Server ne scanne pas en permanence la table des transactions. Alex Kuznetsov a publié ici une solution à un problème de conception similaire, qui complète parfaitement cette réponse.Après avoir lu ces discussions également, je ne vois pas trop pourquoi vous avez choisi la solution DRI plutôt que la plus sensée des autres options que vous décrivez:
Ce type de solution présente d’énormes avantages pratiques si vous avez le luxe de limiter tout accès aux données via votre API transactionnelle. Vous perdez le très important avantage de DRI, à savoir que l'intégrité est garantie par la base de données, mais dans tout modèle suffisamment complexe , certaines règles métier ne peuvent pas être appliquées par DRI .
Dans la mesure du possible, je vous conseillerais d'utiliser DRI pour appliquer des règles métier sans trop plier votre modèle pour rendre cela possible:
Dès que vous envisagez de polluer votre modèle de la sorte, je pense que vous vous dirigez maintenant dans la région où les avantages que présente le DRI sont contrebalancés par les difficultés que vous présentez. Imaginons par exemple qu’un bogue dans votre processus d’archivage puisse en théorie faire en sorte que votre règle d’or (équilibrer toujours la somme des transactions) se brise en silence avec une solution DRI .
Voici un résumé des avantages de l'approche transactionnelle telle que je la vois:
--modifier
Pour permettre l'archivage sans ajouter de complexité ni de risque, vous pouvez choisir de conserver les lignes de résumé dans un tableau de synthèse séparé, généré en continu (emprunté à @Andrew et @Garik).
Par exemple, si les résumés sont mensuels:
la source
Pseudo.
L'idée principale est de stocker les enregistrements de solde et de transaction dans la même table. C'est arrivé historiquement je pensais. Donc, dans ce cas, nous pouvons obtenir un équilibre simplement en localisant le dernier compte rendu.
Une meilleure variante est le nombre décroissant d’enregistrements récapitulatifs. Nous pouvons avoir un enregistrement de solde à la fin (et / ou au début) du jour. Comme vous le savez, chaque banque doit
operational day
ouvrir et fermer ses portes pour effectuer certaines opérations récapitulatives ce jour-là. Cela nous permet de calculer facilement les intérêts en utilisant chaque relevé de solde quotidien, par exemple:La chance.
la source
En fonction de vos besoins, l'option 1 semble être la meilleure. Bien que ma conception permette uniquement les insertions dans la table des transactions. Et avoir le déclencheur sur la table de transaction, pour mettre à jour la table de solde en temps réel. Vous pouvez utiliser les autorisations de base de données pour contrôler l'accès à ces tables.
Dans cette approche, le solde en temps réel est garanti pour être synchronisé avec la table de transaction. Et peu importe si des procédures stockées ou psql ou jdbc sont utilisés. Vous pouvez avoir votre vérification de solde négatif si nécessaire. La performance ne sera pas un problème. Pour obtenir l'équilibre en temps réel, il s'agit d'une requête singleton.
L'archivage n'affectera pas cette approche. Vous pouvez avoir un tableau récapitulatif hebdomadaire, mensuel et annuel également si nécessaire pour des éléments tels que les rapports.
la source
Dans Oracle, vous pouvez le faire en utilisant uniquement la table de transactions avec une vue matérialisée rapide et actualisable qui effectue l'agrégation pour former le solde. Vous définissez le déclencheur dans la vue matérialisée. Si la vue matérialisée est définie avec 'ON COMMIT', elle empêche efficacement l'ajout / la modification de données dans les tables de base. Le déclencheur détecte les données valides [in] et lève une exception, où il annule la transaction. Un bel exemple est ici http://www.sqlsnippets.com/en/topic-12896.html
Je ne sais pas sqlserver mais peut-être qu'il a une option similaire?
la source