Który z tych wzorów stołów jest lepszy pod względem wydajności?

16

Poproszono mnie o stworzenie czegoś, co śledzi dzienny koszt gromadzenia na kontach, i próbuję wymyślić schemat tabeli bazy danych, który by to obsługiwał.

Oto co wiem

  • Firma ma ponad 2,5 miliona kont
  • Spośród nich obecnie pracują średnio 200 000 miesięcznie (co zmienia się wraz z poziomem zatrudnienia, który jest obecnie niski)
  • Mają 13 różnych rodzajów kosztów, które chcieliby śledzić, i ostrzegli, że mogą dodać więcej w przyszłości
  • Chcą, aby koszty były śledzone codziennie
  • Koszty nie są dzielone na cały asortyment. Są one podzielone na liczbę kont obsługiwanych miesięcznie (200 000) lub użytkownicy mogą wprowadzić identyfikatory kont, aby zastosować koszt do grupy kont, lub mogą po prostu określić, dla których kont zastosować koszt.

Moją pierwszą myślą była znormalizowana baza danych:

ID konta
Data
CostTypeId
Ilość

Moim problemem jest robienie matematyki. Ten stół szybko się powiększy. Zakładając, że wszystkie 13 rodzajów kosztów zostanie zastosowanych do wszystkich obsługiwanych kont w bieżącym miesiącu, czyli 200k * 13 * N days in monthokoło 75-80 milionów rekordów miesięcznie lub blisko miliarda rekordów rocznie.

Moją drugą myślą było nieco zdenormalizować to

ID konta
Data
Całkowity koszt
CostType1
CostType2
CostType3
CostType4
CostType5
CostType6
CostType7
CostType8
CostType9
CostType10
CostType11
CostType12
CostType13

Ta metoda jest bardziej zdenormalizowana i może tworzyć do 6 milionów rekordów miesięcznie ( 200k * N days in month) lub około 72 milionów rocznie. Jest to o wiele mniej niż pierwsza metoda, jednak jeśli firma zdecyduje się na nowy typ kosztów w przyszłości, konieczne będzie dodanie kolejnej kolumny bazy danych.

Z dwóch metod, które wolisz? Dlaczego? Czy istnieje inna alternatywa, która mogłaby wymyślić, która poradziłaby sobie z tym lepiej?

Najbardziej interesuje mnie raportowanie wyników, zarówno raportów letnich, jak i szczegółowych. Zadanie, które rozłoży koszty na konta, będzie wykonywane co noc, gdy nikogo nie będzie w pobliżu. Drugim problemem jest rozmiar bazy danych. Istniejąca baza danych ma już prawie 300 GB i uważam, że miejsce na dysku wynosi około 500 GB.

Baza danych to SQL Server 2005

Rachel
źródło
Więc zdobądź kolejny dysk. Dyski są tanie. Możesz mieć 2 TB na koszt spotkania, aby się o to kłócić.

Odpowiedzi:

9

Miliard rekordów rocznie to niewiele.

Z partycjonowaniem (być może dla każdego rodzaju kosztów) i archiwizacją można zarządzać.

Liczba elementów danych do przechowywania wynosi nadal 200 tys. * 13 * N.Jako kolumny otrzymasz mniej wierszy na stronę i zajmie więcej miejsca niż jako wiersze. Możesz zyskać, jeśli „CostType1” nie jest typem danych o stałej długości, ale jest marginalny.

„KISS” jak mówią

gbn
źródło
3
@Rachel Zdecydowanie zalecam wdrożenie schematu partycjonowania z tak dużym zestawem danych. Jeśli skupiają się na pracy i raportowaniu z miesiąca na miesiąc, najlepiej wybrać klucz partycji, który może pokrywać się z tym sposobem myślenia. Ponadto, jeśli poprawnie skonfigurujesz partycję, możesz łatwo przełączać dane między tabelami a tabelami pomostowymi, co powoduje, że duże ładowanie danych i usuwanie zestawów danych jest bardzo szybkie i zajmuje kilka sekund, a nie godzin.
David,
6

Chociaż Twój projekt może z pewnością zmienić noc lub dzień, w tym przypadku skupiłbym się bardziej na indeksach, w tym uwzględnieniu indeksów w razie potrzeby. Chciałbym również przyjrzeć się niektórym narzędziom, które udostępnia SQL Server do obsługi bardzo dużych tabel, takich jak partycjonowanie tabel.

Pomyśl o tym w ten sposób, mimo że w tabeli znajduje się 80 miliardów rekordów, z odpowiednim indeksowaniem, te, które naprawdę jesteś zainteresowany w danym momencie, zostaną zgrupowane fizycznie na dysku. Ze względu na sposób organizacji danych w serwerze SQL dane podzielone według granic indeksu mogą również znajdować się w innej tabeli, ponieważ nie musi czytać całej tabeli, aby uzyskać to, czego potrzebuje.

Jeśli zdecydujesz się także podzielić tabelę na partycje, możesz poprawić czas dostępu i czas wstawiania.


źródło
4

Normalizowałbym się. Przeprowadziliśmy księgowanie kosztów pod kątem rentowności kont klientów w banku i wygenerowaliśmy ponad 250 mln rzędów kosztów indywidualnych, stosując setki sterowników, które zostały przypisane przez centrum kosztów lub księgę główną lub różne inne techniki na milionach kont każdego miesiąca.

Na przykład całkowity koszt obsługi bankomatów został podzielony między konta, które korzystały z bankomatów, na podstawie względnej wielkości wykorzystania. Jeśli więc wydano 1 mln USD na obsługę bankomatów, a tylko 5 klientów skorzystało z niego raz, a jeden klient użył go 5 razy, to ten jeden klient kosztował banku 0,5 mln USD, a pozostali klienci kosztowali banku 1 mln USD każdy. Inne sterowniki mogą być znacznie bardziej złożone.

Ostatecznie prawdopodobnie okaże się, że jest rzadki - niektóre konta nie uzyskują kosztów z niektórych źródeł / sterowników - a niektóre konta nie dostają niczego. W znormalizowanym modelu te wiersze nie istnieją. W modelu zdormalizowanym wiersz istnieje, z pewnymi pustymi kolumnami. Ponadto, w rzadkim znormalizowanym modelu, powinieneś zobaczyć poprawę wydajności, ponieważ istnienie wiersza jest zwykle szybsze do sprawdzenia (z indeksem pokrywającym na CostType) niż sprawdzenie wszystkich wierszy o wartości innej niż NULL w określonym „segmencie” (nawet z indeksy w każdej kolumnie kwoty - co widać, zaczyna się bardzo marnować).

Cade Roux
źródło
SPARSE - To bardzo dobry punkt, który robi różnicę. Jeśli jest rzadki, oszczędzasz miejsce normalizując. W przeciwnym razie nie. Ale miejsce na dysku jest tanie, więc osobiście głosuję za maksymalną elastycznością (znormalizowaną).
3

Niezależnie od korzyści z wydajności zdecydowanie poparłbym wariant 1. Moim zdaniem opcja 2 okradłaby Piotra, by zapłacić Paulowi.


źródło
2

Wybrałbym opcję 1, a następnie, jeśli prędkość raportowania stałaby się problemem w dalszej części drogi, dodałbym również tabelę 2 i umieściłem ją w bazie danych raportowania w jakimś zautomatyzowanym procesie z dnia na dzień / poza szczytem.

Możesz również rozważyć zebranie dziennej struktury tabeli 2 w dalsze tygodniowe, miesięczne, kwartalne, roczne zestawienia, jeśli jest to uzasadnione.

Ale, jak powiedziałem, zdecydowałbym się również przechowywać „surowe” dane w odpowiedniej (znormalizowanej) formie.

EJ Brennan
źródło
0

Biorąc pod uwagę wspomniane woluminy, wybrałbym drugą opcję, ale bez TotalCost. Można powiedzieć, że nadal jest znormalizowany.


Edycja: alternatywnie, w zależności od wymagań i wielkości AccountId, możesz również rozważyć następujące kwestie:

AccountDate
-----------
AccountId  
Date  
AcDtID (surrogate key)

Costs
-------
AcDtID
CostTypeId  
Amount  

Dzięki takiemu projektowi nadal możesz dodać zdormalizowany TotalCost do pierwszej tabeli i dokonać ponownej kalkulacji w nocy, co pozwoli na uruchomienie niektórych raportów tylko na pierwszej tabeli.

Patrick Honorez
źródło
Mam TotalCosttam, ponieważ większość raportów jest podsumowana, i pomyślałem, że szybsze byłoby zapytanie o jedną wartość niż dodanie 13 różnych wartości.
Prawdopodobnie, ale tak naprawdę wprowadzasz zależność przechodnią. Czy te zapisy będą kiedykolwiek aktualizowane? lub po prostu napisane, a następnie tylko czytać?
Zapisy będą aktualizowane za każdym razem, gdy do tego zakresu dat zostaną zastosowane nowe koszty. Po około miesiącu jest mało prawdopodobne, że całkowity koszt zostanie zaktualizowany, ale nadal jest to możliwe z powodu takich rzeczy, jak roczne opłaty za wsparcie.
Następnie każda aktualizacja wymagałaby 2 aktualizacji, a pole TotalCost zwiększa ryzyko niespójności.
Zależność przechodnia, ale niekoniecznie ryzyko niespójności - ograniczenie CHECK () może zagwarantować, że TotalCost jest zawsze sumą kosztów.
Mike Sherrill „Cat Recall”
0

powinieneś właściwie podzielić tabelę jodły na dwie tabele, abyś mógł użyć podzapytania i wybrać drugi wiersz jako kolumnę lub wiele kolumn. jest w ten sposób bardziej elastyczny i dzięki temu można łatwiej uzyskać wynik podobny do drugiego.

Uğur Gümüşhan
źródło