Estoy escribiendo el esquema para una base de datos bancaria simple. Aquí están las especificaciones básicas:
- La base de datos almacenará transacciones contra un usuario y moneda.
- Cada usuario tiene un saldo por moneda, por lo que cada saldo es simplemente la suma de todas las transacciones contra un usuario y moneda determinados.
- Un saldo no puede ser negativo.
La aplicación bancaria se comunicará con su base de datos exclusivamente a través de procedimientos almacenados.
Espero que esta base de datos acepte cientos de miles de transacciones nuevas por día, así como consultas de saldo en un orden de magnitud más alto. Para servir saldos muy rápidamente, necesito agregarlos previamente. Al mismo tiempo, necesito garantizar que un saldo nunca contradiga su historial de transacciones.
Mis opciones son:
Tenga una
balances
mesa separada y realice una de las siguientes acciones:Aplicar transacciones a las tablas
transactions
ybalances
. Usar laTRANSACTION
lógica en mi capa de procedimiento almacenado para garantizar que los saldos y las transacciones estén siempre sincronizados. (Apoyado por Jack )Aplique transacciones a la
transactions
tabla y tenga un activador que actualice labalances
tabla para mí con el monto de la transacción.Aplique transacciones a la
balances
tabla y tenga un activador que agregue una nueva entrada en latransactions
tabla para mí con el monto de la transacción.
Tengo que confiar en enfoques basados en la seguridad para asegurarme de que no se puedan realizar cambios fuera de los procedimientos almacenados. De lo contrario, por ejemplo, algún proceso podría insertar directamente una transacción en la
transactions
tabla y, según el esquema,1.3
el saldo relevante no estaría sincronizado.Tener una
balances
vista indizada que agregue las transacciones de manera adecuada. El motor de almacenamiento garantiza que los saldos estén sincronizados con sus transacciones, por lo que no necesito confiar en enfoques basados en la seguridad para garantizar esto. Por otro lado, ya no puedo exigir que los saldos ya no sean negativos, ya que las vistas, incluso las vistas indexadas, no pueden tenerCHECK
restricciones. (Apoyado por Denny .)Tenga solo una
transactions
tabla pero con una columna adicional para almacenar el saldo efectivo inmediatamente después de la transacción ejecutada. Por lo tanto, el último registro de transacción para un usuario y moneda también contiene su saldo actual. (Sugerido a continuación por Andrew ; variante propuesta por garik .)
Cuando abordé este problema por primera vez, leí estas dos discusiones y decidí la opción 2
. Como referencia, puede ver una implementación básica aquí .
¿Ha diseñado o administrado una base de datos como esta con un perfil de alta carga? ¿Cuál fue su solución a este problema?
¿Crees que he tomado la decisión de diseño correcta? ¿Hay algo que deba tener en cuenta?
Por ejemplo, sé que los cambios de esquema en la
transactions
tabla requerirán que reconstruya labalances
vista. Incluso si estoy archivando transacciones para mantener la base de datos pequeña (por ejemplo, moviéndolas a otro lugar y reemplazándolas con transacciones sumarias), tener que reconstruir la vista de decenas de millones de transacciones con cada actualización de esquema probablemente significará significativamente más tiempo de inactividad por implementación.Si la vista indexada es el camino a seguir, ¿cómo puedo garantizar que ningún saldo sea negativo?
Archivado de transacciones:
Permítanme elaborar un poco sobre el archivo de transacciones y las "transacciones resumidas" que mencioné anteriormente. Primero, el archivado regular será una necesidad en un sistema de alta carga como este. Quiero mantener la coherencia entre los saldos y sus historiales de transacciones al tiempo que permitir que las transacciones antiguas se trasladen a otro lugar. Para hacer esto, reemplazaré cada lote de transacciones archivadas con un resumen de sus montos por usuario y moneda.
Entonces, por ejemplo, esta lista de transacciones:
user_id currency_id amount is_summary
------------------------------------------------
3 1 10.60 0
3 1 -55.00 0
3 1 -12.12 0
se archiva y se reemplaza con esto:
user_id currency_id amount is_summary
------------------------------------------------
3 1 -56.52 1
De esta manera, un saldo con transacciones archivadas mantiene un historial de transacciones completo y consistente.