Я пишу схему для простої банківської бази даних. Ось основні характеристики:
- База даних зберігатиме транзакції проти користувача та валюти.
- У кожного користувача є один залишок на валюту, тому кожен залишок - це просто сума всіх транзакцій проти даного користувача та валюти.
- Баланс не може бути негативним.
Банківська програма зв’язуватиметься зі своєю базою даних виключно через збережені процедури.
Я очікую, що ця база даних може приймати сотні тисяч нових транзакцій на день, а також балансувати запити на більш високий порядок. Для швидкого обслуговування залишків мені потрібно попередньо зібрати їх. У той же час мені потрібно гарантувати, що баланс ніколи не суперечить його історії транзакцій.
Мої варіанти:
Майте окрему
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
Таким чином, баланс із заархівованими транзакціями підтримує повну та послідовну історію транзакцій.