Piszę schemat prostej bazy danych banku. Oto podstawowe specyfikacje:
- Baza danych będzie przechowywać transakcje z użytkownikiem i walutą.
- Każdy użytkownik ma jedno saldo na walutę, więc każde saldo jest po prostu sumą wszystkich transakcji wobec danego użytkownika i waluty.
- Saldo nie może być ujemne.
Aplikacja bankowa będzie komunikować się ze swoją bazą danych wyłącznie poprzez procedury składowane.
Oczekuję, że ta baza danych będzie akceptować setki tysięcy nowych transakcji dziennie, a także wyrównywać zapytania o wyższym rzędzie wielkości. Aby szybko obsłużyć salda, muszę je wstępnie zsumować. Jednocześnie muszę zagwarantować, że saldo nigdy nie będzie sprzeczne z historią transakcji.
Moje opcje to:
Utwórz osobną
balances
tabelę i wykonaj jedną z następujących czynności:Zastosuj transakcje do tabel
transactions
ibalances
. UżyjTRANSACTION
logiki w mojej warstwie procedur składowanych, aby upewnić się, że salda i transakcje są zawsze zsynchronizowane. (Obsługiwane przez Jacka ).Zastosuj transakcje do
transactions
tabeli i uruchom wyzwalacz, który zaktualizujebalances
tabelę dla mnie o kwotę transakcji.Zastosuj transakcje do
balances
tabeli i uruchom wyzwalacz, który dodatransactions
dla mnie nowy wpis w tabeli z kwotą transakcji.
Muszę polegać na podejściach opartych na bezpieczeństwie, aby upewnić się, że nie można wprowadzić żadnych zmian poza procedurami przechowywanymi. W przeciwnym razie, na przykład, jakiś proces mógłby bezpośrednio wstawić transakcję do
transactions
tabeli, a zgodnie ze schematem1.3
odpowiednie saldo nie byłoby zsynchronizowane.Mieć
balances
widok indeksowany, który odpowiednio agreguje transakcje. Mechanizmy magazynowania gwarantują, że salda są zsynchronizowane z ich transakcjami, więc nie muszę polegać na podejściach opartych na bezpieczeństwie, aby to zagwarantować. Z drugiej strony nie mogę już wymuszać, aby salda były nieujemne, ponieważ widoki - nawet widoki indeksowane - nie mogą miećCHECK
ograniczeń. (Obsługiwany przez Denny ).Miej tylko
transactions
tabelę, ale z dodatkową kolumną do przechowywania salda efektywnego zaraz po wykonaniu transakcji. Zatem najnowszy rekord transakcji dla użytkownika i waluty zawiera również ich bieżące saldo. (Sugerowane poniżej przez Andrew ; wariant zaproponowany przez garik .)
Kiedy po raz pierwszy poradziłem sobie z tym problemem, przeczytałem te dwie dyskusje i zdecydowałem się na opcję 2
. W celach informacyjnych możesz zobaczyć jego wdrożenie od podstaw .
Czy zaprojektowałeś lub zarządzałeś taką bazą danych o profilu wysokiego obciążenia? Jakie było twoje rozwiązanie tego problemu?
Czy uważasz, że dokonałem właściwego wyboru projektu? Czy jest coś, o czym powinienem pamiętać?
Na przykład wiem, że zmiany schematu w
transactions
tabeli będą wymagać odbudowaniabalances
widoku. Nawet jeśli archiwizuję transakcje, aby baza danych była niewielka (np. Przenosząc je gdzieś indziej i zastępując je transakcjami podsumowującymi), konieczność odbudowania widoku z dziesiątek milionów transakcji przy każdej aktualizacji schematu prawdopodobnie oznacza znacznie dłuższy czas przestoju na wdrożenie.Jeśli dobrym pomysłem jest widok indeksowany, jak mogę zagwarantować, że saldo nie będzie ujemne?
Archiwizacja transakcji:
Pozwólcie mi rozwinąć nieco kwestię archiwizacji transakcji i wspomnianych powyżej „transakcji podsumowujących”. Po pierwsze, regularna archiwizacja będzie niezbędna w takim systemie o dużym obciążeniu. Chcę zachować spójność między saldami i ich historiami transakcji, jednocześnie umożliwiając przenoszenie starych transakcji gdzie indziej. Aby to zrobić, zastąpię każdą partię zarchiwizowanych transakcji podsumowaniem ich kwot na użytkownika i walutę.
Na przykład ta lista transakcji:
user_id currency_id amount is_summary
------------------------------------------------
3 1 10.60 0
3 1 -55.00 0
3 1 -12.12 0
jest archiwizowany i zastępowany następującym:
user_id currency_id amount is_summary
------------------------------------------------
3 1 -56.52 1
W ten sposób saldo z zarchiwizowanymi transakcjami zachowuje pełną i spójną historię transakcji.