Wydajne przechowywanie 7.300.000.000 wierszy

Jak poradziłbyś sobie z następującym problemem przechowywania i wyszukiwania?

Około 2.000.000 wierszy będzie dodawanych każdego dnia (365 dni w roku) z następującymi informacjami w wierszu:

  • id (niepowtarzalny identyfikator wiersza)
  • entity_id (przyjmuje wartości od 1 do 2.000.000 włącznie)
  • date_id (zwiększany o jeden dzień-przyjmie wartości od 1 do 3.650 (dziesięć lat: 1*365*10))
  • wartość_1 (przyjmuje wartości od 1 do 1.000.000 inclusive)
  • wartość_2 (przyjmuje wartości od 1 do 1.000.000 włącznie)

Entity_id w połączeniu z date_id jest unikalne. W związku z tym do tabeli można dodać co najwyżej jeden wiersz na jednostkę i datę. Baza danych musi być w stanie przechowywać dane dzienne o wartości 10 lat(7.300.000.000 wierszy (3.650*2.000.000)).

To, co opisano powyżej, to wzory zapisu. Wzorzec odczytu jest prosty: wszystkie zapytania będą wykonywane na określonym entity_id. Tzn. pobiera wszystkie wiersze opisujące entity_id = 12345.

Obsługa transakcji nie jest potrzebna, ale rozwiązanie pamięci masowej musi być open-source. Najlepiej korzystam z MySQL, ale jestem otwarty na sugestie.

Teraz-jak poradziłbyś sobie z opisywanym problemem?

aktualizacja: poproszono mnie o rozwinięcie wzorców odczytu i zapisu. Zapisy do tabeli będą wykonywane w jednej partii dziennie, gdzie nowe wpisy 2M będą dodawane za jednym razem. Odczyty będą wykonywane w sposób ciągły z jednym odczytem co sekundę.

Author: knorv, 2009-03-20

7 answers

Użyj partycjonowanie . Z wzorcem odczytu chcesz podzielić przez entity_id hash.

 13
Author: vartec,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-20 10:36:56

"Teraz - jak poradziłbyś sobie z opisywanym problemem?"

Z prostymi plikami płaskimi.

Oto dlaczego

" wszystkie zapytania będą dokonywane na specific entity_id. Tj. odzyskać wszystkie wiersze opisujące entity_id = 12345."

Masz 2.000.000 Bytów. Partycja na podstawie numeru encji:

level1= entity/10000
level2= (entity/100)%100
level3= entity%100

Każdy plik danych to level1/level2/level3/batch_of_data

Możesz następnie odczytać wszystkie pliki w danej części katalogu, aby zwrócić próbki dla przetwarzam.

Jeśli ktoś chce relacyjnej bazy danych, to wczytuje pliki dla danego entity_id do bazy danych w celu ich wykorzystania.


Edit On day numbers.

  1. Na date_id/entity_id reguła unikalności to a nie coś, czym trzeba się zająć. Jest to (a) trywialnie nałożone na nazwy plików i (b) nieistotne dla zapytania.

  2. date_id "rollover" nic nie znaczy - nie ma zapytania, więc nie ma potrzeby zmiany nazwy cokolwiek. date_id powinny po prostu rosnąć bez wiązania z datą epoki. Jeśli chcesz wyczyścić stare dane, usuń stare pliki.

Ponieważ żadne zapytanie nie opiera się na date_id, nic nigdy nie trzeba z nim robić. Może to być nazwa pliku dla wszystkiego, co ma znaczenie.

Aby włączyć date_id do zbioru wynikowego, zapisz go w pliku z pozostałymi czterema atrybutami znajdującymi się w każdym wierszu pliku.


Edit on open/close

Za pisanie, ty trzeba pozostawić plik(y) otwarty. Robisz okresowe spłukiwanie (lub zamykasz / otwierasz), aby upewnić się, że coś naprawdę trafi na dysk.

Masz dwa wyjścia dla architektury twojego pisarza.

  1. Posiada jeden proces" writer", który konsoliduje dane z różnych źródeł. Jest to pomocne, jeśli zapytania są stosunkowo częste. Płacisz za scalanie danych w czasie zapisu.

  2. Mieć kilka plików otwartych jednocześnie do zapisu. Podczas zapytań połącz te pliki w jednym wyniku. Jest to pomocne, gdy zapytania są stosunkowo rzadkie. Płacisz za połączenie danych w momencie zapytania.

 28
Author: S.Lott,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-20 15:04:33

Możesz spojrzeć na te pytania:

Duży klucz podstawowy: 1 + miliard wierszy MySQL + InnoDB?

Duże tabele MySQL

Osobiście chciałbym również pomyśleć o obliczeniu szerokości wiersza, aby dać ci wyobrażenie o tym, jak duża będzie twoja tabela (zgodnie z notatką dotyczącą partycjonowania w pierwszym linku).

HTH.,

S

 5
Author: Simon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:32:49

Twoja aplikacja wydaje się mieć te same cechy, co moja. Napisałem własny silnik pamięci masowej MySQL, aby skutecznie rozwiązać problem. Jest to opisane tutaj

Wyobraź sobie, że Twoje dane są ułożone na dysku jako tablica wpisów o stałej długości 2M (jeden na encję), z których każdy zawiera 3650 wierszy (jeden na dzień) po 20 bajtów (wiersz dla jednego encji na dzień).

Twój wzorzec odczytu odczytuje jeden byt. Jest na dysku więc zajmuje 10MB (ok. 8MB) i odczytuje 3650x20 = około 80K Przy może 100MB / sek ... tak więc odbywa się to w ułamku sekundy, łatwo spełniając swój wzorzec odczytu 1-query-per-second.

Aktualizacja musi zapisywać 20 bajtów w 2m różnych miejscach na dysku. W najprostszym przypadku zajęłoby to 2M*8ms = 4,5 godziny. Jeśli rozłożysz dane na 4 dyski "raid0", może to potrwać 1.125 godzin.

Jednak miejsca są tylko 80K od siebie. Co oznacza, że jest 200 takich miejsc w blok 16MB (typowa wielkość pamięci podręcznej dysku), dzięki czemu może działać nawet 200 razy szybciej. (1 minuta) rzeczywistość jest gdzieś pomiędzy nimi.

Mój silnik pamięci działa w oparciu o taką filozofię, chociaż jest trochę bardziej ogólny niż tablica o stałej długości.

Mógłbyś zakodować dokładnie to, co opisałem. Umieszczenie kodu w silniku MySQL pluggable storage oznacza, że możesz użyć MySQL do odpytywania danych za pomocą różnych generatorów raportów itp.

By the sposób, można wyeliminować date i Entity id z przechowywanego wiersza (ponieważ są one indeksami tablicy) i może być unikalnym id-jeśli naprawdę nie potrzebujesz go ponieważ (entity id, date) jest unikalny, i zapisać 2 wartości jako 3-bajtowy int. Następnie zapisany wiersz ma 6 bajtów i masz 700 aktualizacji na 16M, a zatem szybsze wstawianie i mniejszy plik.

Edit Compare to Flat Files

Zauważyłem, że komentarze ogólne faworyzują pliki płaskie. Nie zapominaj, że katalogi są tylko indeksy zaimplementowane przez system plików i są na ogół zoptymalizowane dla stosunkowo małej liczby stosunkowo dużych pozycji. Dostęp do plików jest na ogół zoptymalizowany tak, że oczekuje stosunkowo niewielkiej liczby plików, aby być otwarte, i ma stosunkowo wysoki narzut na otwieranie i zamykanie, i dla każdego pliku, który jest otwarty. Wszystkie te "relatywnie" odnoszą się do typowego wykorzystania bazy danych.

Używanie nazw systemów plików jako indeksu dla entity-Id, które uważam za nie-rzadkie liczba całkowita od 1 do 2 milionów jest intuicyjna. W programowaniu używałbyś na przykład tablicy, a nie tabeli hashowej, i nieuchronnie ponosisz duże koszty na kosztowną ścieżkę dostępu, która mogłaby być po prostu operacją indeingu tablicy.

Dlatego jeśli używasz plików płaskich, dlaczego nie użyć tylko jednego pliku płaskiego i go indeksować?

Edit {[20] } on performance

Wydajność tej aplikacji będzie zdominowana przez disk seek times. Na obliczenia, które zrobiłem powyżej, określają najlepsze, co możesz zrobić (chociaż możesz przyspieszyć wstawianie, zwalniając SELECT - nie możesz poprawić obu). Nie ma znaczenia, czy używasz bazy danych, plików płaskich, czy jednego pliku płaskiego, z wyjątkiem, że możesz dodać więcej wyszukiwań, których tak naprawdę nie potrzebujesz i spowolnić je dalej. Na przykład indeksowanie (bez względu na to, czy jest to indeks systemu plików, czy indeks bazy danych) powoduje dodatkowe wejścia/Systemy operacyjne w porównaniu z "szukaniem tablicy w górę" , a to spowolni na ziemię.

Edit on benchmark measures

Mam tabelę, która wygląda bardzo podobnie do twojej (lub prawie dokładnie jak jedna z Twoich partycji). Było to 64KB, a nie 2m (1/32 Twojego) i 2788 "dni". Tabela została utworzona w tej samej kolejności wstawiania, w jakiej będzie twoja, i ma ten sam indeks (entity_id, day). Wybór jednej jednostki trwa 20,3 sekundy, aby sprawdzić 2788 dni, co oznacza około 130 poszukiwań na sekundę zgodnie z oczekiwaniami(na dyskach o średnim czasie Wyszukiwania 8 milisekund). Na SELECT time będzie proporcjonalny do liczby dni i niewiele zależy od liczby jednostek. (Będzie to szybsze na dyskach z szybszym czasem wyszukiwania. Używam pary SATA2 w RAID0, ale to nie robi wielkiej różnicy).

Jeśli zmienisz kolejność tabeli na encje ALTER TABLE X ORDER BY (ENTITY, DAY) Następnie ten sam SELECT zajmuje 198 miliseków (ponieważ odczytuje encję zamówienia w jednym dostępie do dysku). Jednak operacja ALTER TABLE trwała 13,98 dni kompletny (dla 182m rzędów).

Jest jeszcze kilka innych rzeczy, o których mówią pomiary 1. Twój plik indeksu będzie tak duży jak Twój plik danych. To jest 3GB dla tego przykładowego stołu. Oznacza to (w moim systemie) cały indeks przy prędkościach dysku, a nie prędkości pamięci.

2.Twój wskaźnik wstawek spadnie logarytmicznie. INSERT do pliku danych jest liniowy, ale insert klucza do indeksu jest log. Przy rekordach 180M dostawałem 153 wstawki na sekundę, co też jest bardzo bliskie poszukiwaniom rate. Pokazuje to, że MySQL aktualizuje blok indeksu liścia dla prawie każdej INSERT (jak można się spodziewać, ponieważ jest indeksowany na encji, ale wstawiany w porządku dziennym.). Tak więc patrzysz na 2M / 153 sekundy= 3.6 godziny, aby zrobić dzienną wstawkę wierszy 2M. (Podzielony przez dowolny efekt, jaki można uzyskać przez partycję między systemami lub dyskami).

 4
Author: Dave Pullin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-24 00:21:40

Miałem podobny problem (choć przy znacznie większej skali-o rocznym zużyciu każdego dnia)

Korzystanie z jednego dużego stołu sprawiło, że przestałem wrzeszczeć - możesz ciągnąć kilka miesięcy, ale myślę, że w końcu go podzielisz.

Nie zapomnij zindeksować tabeli, bo inaczej będziesz mieszał z drobnymi strumieniami danych przy każdym zapytaniu; oh, a jeśli chcesz wykonywać zapytania masowe, użyj płaskich Plików

 2
Author: Dani,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:35

Twój opis odczytanych wzorców nie jest wystarczający. Musisz opisać, jakie ilości danych będą pobierane, jak często i jak duże odchylenia będą występować w zapytaniach.

To pozwoli Ci rozważyć wykonanie kompresji na niektórych kolumnach.

Rozważ również archiwizację i partycjonowanie.

 1
Author: Jonathan Parker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-20 10:43:17

Jeśli chcesz obsługiwać ogromne dane z milionami wierszy, można to uznać za podobną do bazy danych szeregów czasowych, która rejestruje czas i zapisuje dane do bazy danych. Niektóre ze sposobów przechowywania danych są za pomocą InfluxDB i MongoDB.

 0
Author: Arun Raja,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-24 04:07:42