Estou escrevendo o esquema para um banco de dados simples do banco. Aqui estão as especificações básicas:
- O banco de dados armazenará transações contra um usuário e uma moeda.
- Cada usuário tem um saldo por moeda; portanto, cada saldo é simplesmente a soma de todas as transações com um determinado usuário e moeda.
- Um saldo não pode ser negativo.
O aplicativo do banco se comunicará com seu banco de dados exclusivamente por meio de procedimentos armazenados.
Espero que esse banco de dados aceite centenas de milhares de novas transações por dia, além de equilibrar as consultas em uma ordem de magnitude superior. Para atender saldos muito rapidamente, preciso pré-agregá-los. Ao mesmo tempo, preciso garantir que um saldo nunca contradiga seu histórico de transações.
Minhas opções são:
Tenha uma
balances
tabela separada e siga um destes procedimentos:Aplique transações às tabelas
transactions
ebalances
. Use aTRANSACTION
lógica na minha camada de procedimento armazenado para garantir que os saldos e as transações estejam sempre sincronizados. (Suportado por Jack .)Aplique transações à
transactions
tabela e tenha um gatilho que atualize abalances
tabela para mim com o valor da transação.Aplique transações à
balances
tabela e tenha um gatilho que adicione uma nova entrada natransactions
tabela para mim com o valor da transação.
Eu tenho que confiar em abordagens baseadas em segurança para garantir que nenhuma alteração possa ser feita fora dos procedimentos armazenados. Caso contrário, por exemplo, algum processo poderia inserir diretamente uma transação na
transactions
tabela e, no esquema,1.3
o saldo relevante estaria fora de sincronia.Tenha uma
balances
exibição indexada que agregue as transações adequadamente. Os saldos são garantidos pelo mecanismo de armazenamento para permanecer sincronizados com suas transações, portanto, não preciso depender de abordagens baseadas em segurança para garantir isso. Por outro lado, não posso mais impor saldos não negativos, pois as visualizações - mesmo as visualizações indexadas - não podem terCHECK
restrições. (Suportado por Denny .)Tenha apenas uma
transactions
tabela, mas com uma coluna adicional para armazenar o saldo efetivo logo após a transação ser executada. Assim, o último registro de transação para um usuário e moeda também contém seu saldo atual. (Sugerido abaixo por Andrew ; variante proposta por garik .)
Quando lidei com esse problema, li essas duas discussões e decidi pela opção 2
. Para referência, você pode ver uma implementação básica aqui .
Você projetou ou gerencia um banco de dados como este com um perfil de alta carga? Qual foi a sua solução para esse problema?
Você acha que eu fiz a escolha certa para o design? Há algo que eu deva ter em mente?
Por exemplo, eu sei que alterações de esquema na
transactions
tabela exigirão a reconstrução dabalances
exibição. Mesmo que eu esteja arquivando transações para manter o banco de dados pequeno (por exemplo, movendo-as para outro lugar e substituindo-as por transações de resumo), ter que reconstruir a exibição de dezenas de milhões de transações com cada atualização de esquema provavelmente significará significativamente mais tempo de inatividade por implantação.Se a exibição indexada é o caminho a seguir, como posso garantir que nenhum saldo seja negativo?
Arquivando transações:
Deixe-me detalhar um pouco as transações de arquivamento e as "transações de resumo" que mencionei acima. Primeiro, o arquivamento regular será uma necessidade em um sistema de alta carga como esse. Desejo manter a consistência entre os saldos e o histórico de transações, permitindo que as transações antigas sejam movidas para outro lugar. Para fazer isso, substituirei cada lote de transações arquivadas por um resumo de seus valores por usuário e moeda.
Então, por exemplo, esta lista de transações:
user_id currency_id amount is_summary
------------------------------------------------
3 1 10.60 0
3 1 -55.00 0
3 1 -12.12 0
é arquivado e substituído por este:
user_id currency_id amount is_summary
------------------------------------------------
3 1 -56.52 1
Dessa forma, um saldo com transações arquivadas mantém um histórico de transações completo e consistente.