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_block
tabeli.
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, timestamp
a mac
ponieważ 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 JOIN
z tabelami danych. Oto EXPLAIN ANALYZE
zapytanie 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
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?
źródło
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.
źródło