Я пишу схему для простой банковской базы данных. Вот основные характеристики:
- База данных будет хранить транзакции против пользователя и валюты.
- У каждого пользователя есть один баланс на валюту, поэтому каждый баланс - это просто сумма всех транзакций с данным пользователем и валютой.
- Баланс не может быть отрицательным.
Приложение банка будет связываться с базой данных исключительно через хранимые процедуры.
Я ожидаю, что эта база данных будет принимать сотни тысяч новых транзакций в день, а также балансировать запросы более высокого порядка. Чтобы быстро подвести баланс, мне нужно предварительно агрегировать их. В то же время я должен гарантировать, что баланс никогда не противоречит истории его транзакций.
Мои варианты:
Создайте отдельную
balancesтаблицу и выполните одно из следующих действий:Применить операции для обоих
transactionsиbalancesтаблиц. ИспользуйтеTRANSACTIONлогику в слое моей хранимой процедуры, чтобы гарантировать, что сальдо и транзакции всегда синхронизированы. (При поддержке Джек .)Примените транзакции к
transactionsтаблице и получите триггер, который обновитbalancesдля меня таблицу с суммой транзакции.Примените транзакции к
balancesтаблице и получите триггер, который добавляет новую запись вtransactionsтаблицу для меня с суммой транзакции.
Я должен полагаться на подходы, основанные на безопасности, чтобы убедиться, что никакие изменения не могут быть внесены за пределы хранимых процедур. В противном случае, например, какой-то процесс мог бы напрямую вставить транзакцию в
transactionsтаблицу, и по схеме1.3соответствующий баланс был бы не синхронизирован.Иметь
balancesиндексированное представление, которое соответствующим образом агрегирует транзакции. Механизмы хранения гарантируют, что сальдо будет синхронизировано с их транзакциями, поэтому мне не нужно полагаться на подходы, основанные на безопасности, чтобы гарантировать это. С другой стороны, я больше не могу принудить баланс быть неотрицательным, так как представления - даже индексированные представления - не могут иметьCHECKограничений. (При поддержке Денни .)Имейте только
transactionsтаблицу, но с дополнительным столбцом для хранения баланса, действующего сразу после выполнения этой транзакции. Таким образом, последняя запись транзакции для пользователя и валюты также содержит их текущий баланс. ( Предложено Эндрю ниже ; вариант предложен Гариком .)
Когда я впервые решил эту проблему, я прочитал эти две дискуссии и выбрал вариант 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
Таким образом, баланс с архивированными транзакциями поддерживает полную и непротиворечивую историю транзакций.