Я пишу схему для простої банківської бази даних. Ось основні характеристики:
- База даних зберігатиме транзакції проти користувача та валюти.
- У кожного користувача є один залишок на валюту, тому кожен залишок - це просто сума всіх транзакцій проти даного користувача та валюти.
- Баланс не може бути негативним.
Банківська програма зв’язуватиметься зі своєю базою даних виключно через збережені процедури.
Я очікую, що ця база даних може приймати сотні тисяч нових транзакцій на день, а також балансувати запити на більш високий порядок. Для швидкого обслуговування залишків мені потрібно попередньо зібрати їх. У той же час мені потрібно гарантувати, що баланс ніколи не суперечить його історії транзакцій.
Мої варіанти:
Майте окрему
balancesтаблицю і виконайте одну з наступних дій:Застосовуйте транзакції як до, так
transactionsі доbalancesтаблиць. ВикористовуйтеTRANSACTIONлогіку в моєму шарі збережених процедур, щоб гарантувати, що баланси та транзакції завжди синхронізовані. (За підтримки Джека .)Застосовуйте транзакції до
transactionsтаблиці та майте тригер, який оновлюєbalancesтаблицю для мене на суму транзакції.Застосовуйте транзакції до
balancesтаблиці та майте тригер, який додаєtransactionsдля мене новий запис із сумою транзакції.
Мені доводиться покладатися на підходи, засновані на безпеці, щоб переконатися, що жодні зміни не можуть бути внесені поза збереженими процедурами. В іншому випадку, наприклад, якийсь процес може безпосередньо вставити транзакцію в
transactionsтаблицю, і за схемою1.3відповідний баланс не синхронізується.Майте
balancesіндексований вигляд, який відповідним чином агрегує транзакції. Система зберігання даних гарантує баланс, щоб він синхронізувався зі своїми транзакціями, тому мені не потрібно покладатися на підходи, засновані на безпеці, щоб гарантувати це. З іншого боку, я не можу примусити залишки бути негативними, оскільки погляди - навіть індексовані - не можуть матиCHECKобмежень. (За підтримки Денні .)Майте просто
transactionsтаблицю, але з додатковим стовпцем, щоб зберігати баланс, що діє після закінчення транзакції. Таким чином, останній запис транзакцій для користувача та валюти також містить їх поточний баланс. (Запропоновано Андрієм нижче ; варіант, запропонований garik .)
Коли я вперше вирішив цю проблему, я прочитав ці дві дискусії та визначився з варіантом 2. Для довідки ви можете ознайомитись із його реалізацією з голими кістками тут .
Ви створили та керували такою базою даних із профілем високого навантаження? Яким було ваше вирішення цієї проблеми?
Як ви вважаєте, я зробив правильний вибір дизайну? Чи варто щось пам’ятати?
Наприклад, я знаю, що зміни схеми в
transactionsтаблиці вимагатимуть відновленняbalancesподання. Навіть якщо я архівую транзакції, щоб зберегти базу даних невеликими (наприклад, перемістивши їх кудись іншим і замінивши їх підсумковими транзакціями), доведеться відновлювати перегляд десятків мільйонів транзакцій з кожним оновленням схеми, ймовірно, означатиме значно більше простоїв на розгортання.Якщо індексований вигляд - це шлях, як я можу гарантувати відсутність балансу негативного?
Архівні транзакції:
Дозвольте трохи зупинитися на архівуванні транзакцій та "зведених транзакціях", про які я згадував вище. По-перше, регулярне архівування буде необхідним у такій системі з високим навантаженням. Я хочу зберегти узгодженість між залишками та їх історіями трансакцій, дозволяючи перенести старі транзакції кудись в інше місце. Для цього я заміню кожну партію архівованих транзакцій підсумком їх суми на користувача та валюти.
Так, наприклад, цей перелік операцій:
user_id currency_id amount is_summary
------------------------------------------------
3 1 10.60 0
3 1 -55.00 0
3 1 -12.12 0
архівується геть і замінюється цим:
user_id currency_id amount is_summary
------------------------------------------------
3 1 -56.52 1
Таким чином, баланс із заархівованими транзакціями підтримує повну та послідовну історію транзакцій.