Da ich ein junger Entwickler bin und nicht wirklich mit Datenbanken umgehen kann (PostgreSQL 9.3), bin ich auf einige Probleme mit einem Projekt gestoßen, bei dem ich wirklich Hilfe brauche.
In meinem Projekt geht es darum, Daten von Geräten (bis zu 1000 oder mehr Geräten) zu sammeln, wobei jedes Gerät pro Sekunde einen Datenblock sendet, was ungefähr 3 Millionen Zeilen pro Stunde ergibt.
Derzeit habe ich eine große Tabelle, in der ich die eingehenden Daten jedes Geräts speichere:
CREATE TABLE data_block(
id bigserial
timestamp timestamp
mac bigint
)
Da es mehrere Arten von Daten gibt, die ein Datenblock enthalten kann (oder nicht), gibt es andere Tabellen, die auf die data_block
Tabelle verweisen .
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);
...
Es ist möglich, dass in einem Datenblock 3x DatenA, 1x DatenB, aber keine DatenC vorhanden sind.
Die Daten werden einige Wochen lang aufbewahrt, daher werden in dieser Tabelle ca. 5 Milliarden Zeilen enthalten sein. Im Moment habe ich ~ 600 Millionen Zeilen in der Tabelle und meine Abfragen dauern sehr lange. Also habe ich beschlossen, einen Index über timestamp
und zu mac
erstellen, da meine select-Anweisungen immer über die Zeit und oft auch über die Zeit + mac abfragen.
CREATE INDEX index_ts_mac ON data_block (timestamp DESC, mac);
... aber meine Fragen dauern noch ewig. Zum Beispiel habe ich Daten für einen Tag und einen Mac abgefragt:
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
Ich habe vor dem Ausführen der Abfrage ein vollständiges Vakuum durchgeführt. Gibt es eine elegante Möglichkeit, ein solches Problem mit großen Tabellen zu lösen, um eine Abfrage <10 Sekunden durchzuführen?
Ich habe über Partitionierung gelesen, aber dies funktioniert nicht mit meinen dataA-, dataB- und dataC-Verweisen auf data_block_id, oder? Wenn es irgendwie funktionieren würde, sollte ich im Laufe der Zeit oder über den Mac Partitionen erstellen?
Ich habe meinen Index in die andere Richtung geändert. Erst MAC, dann Zeitstempel, und es gewinnt viel Leistung.
CREATE INDEX index_mac_ts ON data_block (mac, timestamp DESC);
Abfragen dauern jedoch> 30 Sekunden. Besonders wenn ich LEFT JOIN
mit meinen Datentabellen ein mache . Hier ist eine EXPLAIN ANALYZE
der Abfragen mit dem neuen Index:
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
Leider ist meine Hardware streng limitiert. Ich verwende einen Intel i3-2100 mit 3,10 GHz und 4 GB RAM. Meine aktuellen Einstellungen sind wie folgt:
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
Ich arbeitete an einer Anwendung, die Milliarden von Messwerten von Stromzählern enthielt, und führte die meisten Abfragen in weniger als 10 Sekunden aus.
Unsere Umgebung war anders. Microsoft SQL Server auf einem Computer der Serverklasse (4 Kerne, 24 GB Speicher). Gibt es eine Möglichkeit, auf einen Server zu aktualisieren?
Ein großes Problem ist, dass die Erfassung der Messwerte nacheinander einen großen Einfluss auf die Leistung der Datenbank hatte. Das Schreiben von Daten erforderte Sperren und Abfragen würde warten. Können Sie Einsätze in Chargen machen?
Mit Ihrem Schema haben Sie 4 sehr große Tabellen. Es ist wichtig, dass alle Ihre Joins Indizes für beide Tabellen verwenden. Ein Tabellenscan wird ewig dauern. Ist es möglich, sie mit nullfähigen Feldern zu einer Tabelle zusammenzuführen?
quelle
Sie stoßen an die inhärenten Skalierbarkeitsgrenzen von Postgres (oder einem anderen RDBMS).
Denken Sie daran, dass ein RDBMS-Index ein B-Baum ist. Ein B-Baum ist O (log n) sowohl für den Durchschnitts- als auch für den Worst-Case. Dies macht es zu einer guten, sicheren und vorhersehbaren Wahl für vernünftige Werte von N. Es bricht zusammen, wenn N zu groß wird.
NoSQL-Datenbanken sind (größtenteils) Hash-Tabellen. Eine Hash-Tabelle ist im Durchschnitt O (1) und im schlimmsten Fall O (n). Vorausgesetzt, Sie können den schlimmsten Fall vermeiden, funktioniert er für sehr große Werte von N sehr gut.
Darüber hinaus ist eine Hash-Tabelle leicht zu parallelisieren und ein B-Baum nicht. Dies macht Hash-Tabellen für eine verteilte Computerarchitektur besser geeignet.
Wenn Sie anfangen, zu Milliarden Zeilentabellen zu gelangen, ist es an der Zeit, über einen Wechsel von RDBMS zu NoSQL nachzudenken. Cassandra wäre wahrscheinlich eine gute Wahl für Ihren Anwendungsfall.
quelle