Langsame Abfragen in der Milliarden-Zeilen-Tabelle // Index verwendet

10

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_blockTabelle 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 timestampund zu macerstellen, 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 JOINmit meinen Datentabellen ein mache . Hier ist eine EXPLAIN ANALYZEder 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
Mann
quelle

Antworten:

1

Dies spiegelt möglicherweise meine MS SQL-Tendenz wider, aber ich würde versuchen, die Tabelle nach zu gruppieren timestamp. Wenn Sie häufig Daten für einen bestimmten Zeitraum abrufen, ist dies hilfreich, da die Daten physisch zusammenhängend gespeichert werden. Das System kann nach dem Startpunkt suchen, bis zum Ende des Bereichs scannen und fertig sein. Wenn Sie für eine bestimmte Stunde abfragen, sind das nur 3.600.000 Datensätze.

Wenn Ihre Anfrage (was ist ...?) Für eine bestimmte Maschine ist, muss Postgres 99,9% dieser 3,6 Millionen Datensätze herausfiltern. Wenn dieser Eins-zu-Tausend-Filter selektiver ist als ein typischer Datumsbereichs-Fitler, sollten Sie das selektivere macFeld als erste Komponente Ihres Index verwenden. Es kann sich immer noch lohnen, Cluster zu erstellen.

Wenn dies immer noch nicht der Fall ist, würde ich nach demselben Feld partitionieren, das Sie indizieren, entweder timestampoder mac.

Sie haben die Datentypen nicht angegeben. Sind sie den Daten angemessen? Wenn Sie Datumsangaben als Text speichern, wird Ihre Tabelle beispielsweise unnötig aufgebläht.

Jon aller Berufe
quelle
2
Postgres haben keine Clustered - Indizes (obwohl es kann Cluster einen Tisch entlang eines Index - aber das muss manuell getan werden und wird nicht „stay“)
a_horse_with_no_name
Danke für den Ratschlag. Jetzt läuft es schneller als zuvor, aber immer noch mit einer sehr geringen Leistung> 30 Sekunden pro Abfrage. Ich habe auch Clustering gemacht, aber wie @a_horse_with_no_name sagte: In Postgres ist dies ein One-Shot. Ich denke, meine Datentypen stimmen. Ich fügte sie in der Frage
Manman
Ohne gruppierte Tabellen wäre meine nächste Empfehlung für Bereichsabfragen die Partitionierung.
Jon of All Trades
-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?

KC-NH
quelle
Einfügungen in Stapeln: Ich könnte Masseneinfügungen machen, aber im Moment arbeite ich an einer Testdatenbank, in der überhaupt keine Einfügungen vorgenommen werden, während eine Abfrage ausgeführt wird. aber danke, daran werde ich später denken :) Indizes: Ich habe Indizes für jede Tabelle. auf den Datentabellen einen Index auf die ID, auf die data_block-Tabelle auf (mac, timestamp). Das Problem ist auch da, wenn ich nach Daten A pro Linksverknüpfung suche, aber es gibt keine. Auch mit Index durchsucht es die Datentabellen. nullfähige Felder: sind nicht möglich, da ein Datenblock mehr als eine Datenart haben kann. 1xdata_block -> 4xdataA zB
manman
Gibt Ihnen Ihr DB-Tool einen Abfrageanalysator? Möglicherweise benötigen Sie einen Index für data_block basierend auf der ID.
KC-NH
Ich werde es versuchen, aber ich verstehe nicht, warum das helfen kann!?
Manman
-2

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.

Professor Photon
quelle
2
Viele RDBMS bieten viel mehr Optionen als B-Tree-Indizes (Hash, Bitmap und andere). Einige DBMS speichern Zeilen und andere Spalten. Und O (logn) ist nicht schlecht, selbst für Milliarden von Zeilen. Und sie können unmöglich ein Limit erreichen, wenn sie einen 4-GB-Speicher verwenden.
Ypercubeᵀᴹ