Я пишу схему для простой банковской базы данных. Вот основные характеристики:
- База данных будет хранить транзакции против пользователя и валюты.
- У каждого пользователя есть один баланс на валюту, поэтому каждый баланс - это просто сумма всех транзакций с данным пользователем и валютой.
- Баланс не может быть отрицательным.
Приложение банка будет связываться с базой данных исключительно через хранимые процедуры.
Я ожидаю, что эта база данных будет принимать сотни тысяч новых транзакций в день, а также балансировать запросы более высокого порядка. Чтобы быстро подвести баланс, мне нужно предварительно агрегировать их. В то же время я должен гарантировать, что баланс никогда не противоречит истории его транзакций.
Мои варианты:
Создайте отдельную
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
Таким образом, баланс с архивированными транзакциями поддерживает полную и непротиворечивую историю транзакций.