Użyto powolnych zapytań o miliard-wierszy-tabela // indeks

10

Ponieważ jestem młodym programistą i nie bardzo dobrze posługuję się bazami danych (PostgreSQL 9.3), napotkałem pewne problemy z projektem, w którym naprawdę potrzebuję pomocy.

Mój projekt dotyczy gromadzenia danych z urządzeń (do 1000 lub więcej urządzeń), gdzie każde urządzenie wysyła jeden blok danych co sekundę, co daje około 3 milionów wierszy na godzinę.

Obecnie mam jedną dużą tabelę, w której przechowuję przychodzące dane z każdego urządzenia:

CREATE TABLE data_block(
    id bigserial
    timestamp timestamp
    mac bigint
)

Ponieważ istnieje kilka rodzajów danych, które blok danych może (lub nie może) zawierać, istnieją inne tabele, które odwołują się do data_blocktabeli.

CREATE TABLE dataA(
    data_block_id bigserial
    data

    CONSTRAINT fkey FOREIGN KEY (data_block_id) REFERENCES data_block(id);
);
CREATE TABLE dataB(...);
CREATE TABLE dataC(...);
CREATE INDEX index_dataA_block_id ON dataA (data_block_id DESC);
...

Możliwe jest, że w jednym bloku danych znajduje się 3 x dane A, 1 x dane B, ale brak danych C.

Dane będą przechowywane przez kilka tygodni, więc w tej tabeli mam około 5 miliardów wierszy. W tej chwili mam około 600 milionów wierszy w tabeli, a moje zapytania zajmują naprawdę dużo czasu. Postanowiłem więc utworzyć indeks ponad, timestampa macponieważ moje instrukcje select zawsze sprawdzają w czasie, a często także w czasie + mac.

CREATE INDEX index_ts_mac ON data_block (timestamp DESC, mac);

... ale moje zapytania wciąż trwają wieki. Na przykład przesłuchałem dane dla jednego dnia i jednego komputera Mac:

SELECT * FROM data_block 
WHERE timestamp>'2014-09-15' 
AND timestamp<'2014-09-17' 
AND mac=123456789
Index Scan using index_ts_mac on data_block  (cost=0.57..957307.24 rows=315409 width=32) (actual time=39.849..334534.972 rows=285857 loops=1)
  Index Cond: ((timestamp > '2014-09-14 00:00:00'::timestamp without time zone) AND (timestamp < '2014-09-16 00:00:00'::timestamp without time zone) AND (mac = 123456789))
Total runtime: 334642.078 ms

Zrobiłem pełną próżnię przed uruchomieniem zapytania. Czy istnieje elegancki sposób rozwiązania takiego problemu z dużymi tabelami w celu wykonania zapytania <10 sekund?

Czytałem o partycjonowaniu, ale to nie będzie działać z moimi danymi A, dataB, dataC odwołania do data_block_id, prawda? Jeśli to jakoś zadziała, czy powinienem tworzyć partycje w czasie czy na Macu?

Zmieniłem indeks na inny kierunek. Najpierw MAC, potem znacznik czasu i zyskuje dużo wydajności.

CREATE INDEX index_mac_ts ON data_block (mac, timestamp DESC);

Nadal jednak zapytania zajmują> 30 sekund. Zwłaszcza, gdy robię LEFT JOINz tabelami danych. Oto EXPLAIN ANALYZEzapytanie z nowym indeksem:

EXPLAIN ANALYZE SELECT * FROM data_block WHERE mac = 123456789 AND timestamp < '2014-10-05 00:00:00' AND timestamp > '2014-10-04 00:00:00'
Bitmap Heap Scan on data_block  (cost=1514.57..89137.07 rows=58667 width=28) (actual time=2420.842..32353.678 rows=51342 loops=1)
  Recheck Cond: ((mac = 123456789) AND (timestamp < '2014-10-05 00:00:00'::timestamp without time zone) AND (timestamp > '2014-10-04 00:00:00'::timestamp without time zone))
  ->  Bitmap Index Scan on index_mac_ts  (cost=0.00..1499.90 rows=58667 width=0) (actual time=2399.291..2399.291 rows=51342 loops=1)
        Index Cond: ((mac = 123456789) AND (timestamp < '2014-10-05 00:00:00'::timestamp without time zone) AND (timestamp > '2014-10-04 00:00:00'::timestamp without time zone))
Total runtime: 32360.620 ms 

Niestety mój sprzęt jest ściśle ograniczony. Korzystam z procesora Intel i3-2100 @ 3,10 GHz, 4 GB pamięci RAM. Moje obecne ustawienia są następujące:

default_statistics_target = 100
maintenance_work_mem = 512MB
constraint_exclusion = on
checkpoint_completion_target = 0.9
effective_cache_size = 4GB
work_mem = 512MB
wal_buffers = 16MB
checkpoint_segments = 32
shared_buffers = 2GB
max_connections = 20
random_page_cost = 2
manman
źródło

Odpowiedzi:

1

Może to odzwierciedlać moją stronniczość MS SQL, ale spróbuję utworzyć klaster według tabeli timestamp. Jeśli często pobierasz dane przez określony czas, pomoże to, ponieważ dane będą fizycznie przechowywane w sposób ciągły. System może wyszukiwać do punktu początkowego, skanować do końca zakresu i gotowe. Jeśli pytasz o konkretną godzinę, to tylko 3 600 000 rekordów.

Jeśli twoje zapytanie (które jest ...?) Dotyczy konkretnego komputera, Postgres będzie musiał odfiltrować 99,9% z tych 3,6 mln rekordów. Jeśli ten filtr jeden na tysiąc jest bardziej selektywny niż typowy moduł dopasowujący zakres dat, należy zastosować bardziej selektywne macpole jako pierwszy składnik indeksu. Może być nadal warte grupowania.

Jeśli to nadal nie robi tego, bym podzielić według tej samej dziedzinie jesteś indeksowanie, albo timestampalbo mac.

Nie podałeś typów danych. Czy są odpowiednie do danych? Na przykład przechowywanie dat jako tekstu niepotrzebnie powoduje powiększenie stołu.

Jon of All Trades
źródło
2
Postgres nie zostały skupione indeksów (chociaż może klastra stolik wzdłuż indeksu - ale to musi być wykonywane ręcznie i nie będzie „pobyt”)
a_horse_with_no_name
dziękuję za radę. teraz działa szybciej niż wcześniej, ale nadal ma bardzo niską wydajność> 30 sekund na zapytanie. Zrobiłem także tworzenie klastrów, ale jak powiedział @a_horse_with_no_name: w postgresie jest to jednorazowy strzał. myślę, że moje typy danych są prawidłowe. dodałem je do pytania
manman
Bez tabel klastrowych moją następną rekomendacją dla zapytań o zakres byłoby partycjonowanie.
Jon of All Trades
-2

Pracowałem nad aplikacją, która miała miliardy odczytów z liczników elektrycznych i wykonywała większość zapytań w czasie znacznie poniżej 10 sekund.

Nasze środowisko było inne. Microsoft SQL Server na maszynie klasy serwerowej (4 rdzenie, pamięć 24 GB). Czy jest jakaś szansa na uaktualnienie do serwera?

Jednym wielkim problemem jest to, że przyjmowanie odczytów pojedynczo miało duży wpływ na wydajność bazy danych. Zapisywanie danych wymagało blokady i zapytań. Czy potrafisz wstawiać partie?

Ze swoim schematem będziesz mieć 4 bardzo duże stoły. Ważne będzie, aby wszystkie twoje połączenia korzystały z indeksów w obu tabelach. Skanowanie tabeli zajmie wieczność. Czy jest możliwe połączenie ich w 1 tabelę z polami o zerowej zdolności?

KC-NH
źródło
wstawki w partiach: mogłem robić wstawianie zbiorcze, ale w tej chwili pracuję nad testową bazą danych, w której wstawianie nie jest wykonywane w ogóle podczas działania zapytania. ale dziękuję, pomyślę o tym później :) indeksy: mam indeksy na każdym stole. w tabelach danych indeks identyfikatora, w tabeli data_block na (mac, sygnatura czasowa). problem pojawia się również, gdy szukam danych na lewe połączenie, ale nie ma. nawet z indeksem przeszukuje tabele danych. pola dopuszczające wartości zerowe: nie są możliwe, ponieważ blok danych może zawierać więcej niż jeden rodzaj danych. 1xdata_block -> 4xdataA np.
manman
Czy Twoje narzędzie DB udostępnia analizator zapytań? Może być potrzebny indeks na data_block na podstawie identyfikatora.
KC-NH
Spróbuję, ale nie rozumiem, dlaczego to może pomóc !?
manman
-2

Uderzasz w wewnętrzne limity skalowalności Postgres (lub dowolnego innego RDBMS).

Pamiętaj, że indeks RDBMS to B-Tree. B-Tree to O (log n) zarówno dla średniego, jak i najgorszego przypadku. To sprawia, że ​​jest to miły, bezpieczny i przewidywalny wybór dla rozsądnych wartości N. Rozbija się, gdy N staje się zbyt duży.

Bazy danych NoSQL to (w przeważającej części) tabele skrótów. Tablica skrótów to O (1) w średnim przypadku i O (n) w najgorszym przypadku. Zakładając, że możesz uniknąć najgorszego przypadku, działa on naprawdę dobrze dla bardzo dużych wartości N.

Ponadto tabela skrótów jest łatwa do zrównoleglenia, a b-drzewo nie. To sprawia, że ​​tabele skrótów są bardziej odpowiednie dla rozproszonej architektury obliczeniowej.

Kiedy zaczniesz docierać do miliarda tabel wierszy, nadszedł czas, aby rozważyć przejście z RDBMS na NoSQL. Cassandra prawdopodobnie byłaby dobrym wyborem dla twojego przypadku użycia.

Profesor Photon
źródło
2
Wiele RDBMS ma o wiele więcej opcji niż indeksy B-drzewa (skrót, bitmapa i inne). Niektóre DBMS przechowują wiersze, a niektóre przechowują kolumny. A O (logn) nie jest złe, nawet dla miliardów wierszy. I nie mogą przekroczyć żadnego limitu, gdy używają urządzenia pamięci 4 GB.
ypercubeᵀᴹ