Ich arbeite an einem Projekt, das Daten in Batches verarbeitet und eine PostgreSQL-Datenbank (9.6, aber ich könnte upgraden) füllt. Die aktuelle Funktionsweise besteht darin, dass der Prozess in getrennten Schritten abläuft und jeder Schritt Daten zu einer Tabelle hinzufügt, die er besitzt (selten schreiben zwei Prozesse in derselben Tabelle, wenn sie dies tun, schreiben sie in eine andere Spalte).Eine PostgreSQL-Datenbank aufteilen/teilen
Wie die Daten sind, werden die Daten bei jedem Schritt immer feinkörniger. Als vereinfachtes Beispiel habe ich eine Tabelle, die die Datenquellen definiert. Es gibt sehr wenige (in den Zehner-/niedrigen Hunderten), aber jede dieser Datenquellen erzeugt Stapel von Datenproben (Stapel und Proben sind separate Tabellen, um Metadaten zu speichern). Jede Charge erzeugt typischerweise etwa 50.000 Proben. Jeder dieser Datenpunkte wird dann Schritt für Schritt verarbeitet, und jeder Datenabtastwert erzeugt mehr Datenpunkte in der nächsten Tabelle.
Dies funktionierte gut, bis wir zu einer 1,5mil Reihen in der Probentabelle kamen (das ist aus unserer Sicht nicht viele Daten). Das Filtern nach einem Batch beginnt langsam zu werden (ungefähr 10 ms für jede Probe, die wir abrufen). Und es fängt an, ein großer Flaschenhals zu werden, weil die Ausführungszeit, um die Daten für einen Stapel zu erhalten, 5-10min dauert (der Abruf ist ms).
Wir haben B-Tree-Indizes für alle Fremdschlüssel, die für diese Abfragen beteiligt sind.
Da unsere Berechnungen auf die Batches abzielen, muss ich während der Berechnung normalerweise nicht zwischen Batches abfragen (dies ist der Moment, in dem die Abfragezeit sehr schmerzt). Aus Gründen der Datenanalyse müssen Ad-hoc-Abfragen über mehrere Stapel hinweg möglich bleiben.
Also eine sehr einfache Lösung wäre, eine individuelle Datenbank für jeden Stapel zu generieren, und irgendwie Abfrage über diese Datenbanken, wenn ich muss. Wenn ich nur eine Charge in jeder Datenbank hätte, wäre die Filterung für eine einzelne Charge offensichtlich sofort und mein Problem wäre gelöst (vorerst). Dann würde ich jedoch mit Tausenden von Datenbanken enden und die Datenanalyse wäre schmerzhaft.
In PostgreSQL gibt es eine Möglichkeit, so zu tun, dass ich separate Datenbanken für einige Abfragen habe? Idealerweise möchte ich das für jede Charge tun, wenn ich eine neue Charge "registriere".
Außerhalb der Welt von PostgreSQL, gibt es eine andere Datenbank, die ich für meinen Anwendungsfall versuchen sollte?
Edit: DDL/Schema
In unserer aktuellen Implementierung sample_representation
ist die Tabelle, die alle Verarbeitungsergebnisse abhängen. Ein Stapel ist wirklich definiert durch ein Tupel von (batch.id
, representation.id
). Die Abfrage, die ich und versuchte, wie oben langsam beschrieben ist (10 ms für jede Probe, Hinzufügen von bis zu etwa 5 min für 50k Proben)
SELECT sample_representation.id, sample.sample_pos
FROM sample_representation
JOIN sample ON sample.id = sample_representation.id_sample
WHERE sample_representation.id_representation = 'representation-uuid' AND sample.id_batch = 'batch-uuid'
Aktuell haben wir irgendwo um 1,5 s sample
s, 2 representation
s, 460 batch
es (von denen 49 verarbeitet wurden, den anderen sind keine Proben zugeordnet), was bedeutet, dass jede Charge im Durchschnitt 30.000 Proben hat. Manche haben ungefähr 50k.
Das Schema ist unten. Mit allen Tabellen sind einige Metadaten verknüpft, aber in diesem Fall werde ich nicht danach gefragt. Die eigentlichen Beispieldaten werden separat auf der Festplatte und nicht in der Datenbank gespeichert, falls dies einen Unterschied macht.
create table batch
(
id uuid default uuid_generate_v1mc() not null
constraint batch_pk
primary key,
path text not null
constraint unique_batch_path
unique,
id_data_source uuid
)
;
create table sample
(
id uuid default uuid_generate_v1mc() not null
constraint sample_pk
primary key,
sample_pos integer,
id_batch uuid
constraint batch_fk
references batch
on update cascade on delete set null
)
;
create index sample_sample_pos_index
on sample (sample_pos)
;
create index sample_id_batch_sample_pos_index
on sample (id_batch, sample_pos)
;
create table representation
(
id uuid default uuid_generate_v1mc() not null
constraint representation_pk
primary key,
id_data_source uuid
)
;
create table data_source
(
id uuid default uuid_generate_v1mc() not null
constraint data_source_pk
primary key
)
;
alter table batch
add constraint data_source_fk
foreign key (id_data_source) references data_source
on update cascade on delete set null
;
alter table representation
add constraint data_source_fk
foreign key (id_data_source) references data_source
on update cascade on delete set null
;
create table sample_representation
(
id uuid default uuid_generate_v1mc() not null
constraint sample_representation_pk
primary key,
id_sample uuid
constraint sample_fk
references sample
on update cascade on delete set null,
id_representation uuid
constraint representation_fk
references representation
on update cascade on delete set null
)
;
create unique index sample_representation_id_sample_id_representation_uindex
on sample_representation (id_sample, id_representation)
;
create index sample_representation_id_sample_index
on sample_representation (id_sample)
;
create index sample_representation_id_representation_index
on sample_representation (id_representation)
;
'... 50k Proben. Jeder dieser Datenpunkte wird dann Schritt für Schritt verarbeitet ... 'Sie meinen: Sie holen einen Punkt, dann den nächsten Punkt, dann den nächsten Punkt, bis zu 50K mal? Warum nicht alle auf einmal in einem großen Schwung? – joop
"* In PostgreSQL gibt es eine Möglichkeit, so zu tun, dass ich separate Datenbanken für einige Abfragen * habe" - Sie könnten [shard] (https://en.wikipedia.org/wiki/Shard_ (database_architecture)) die Daten zu anderen Datenbankserver und greifen Sie transparent auf einen Server zu, indem Sie [fremde Tabellen] (https://www.postgresql.org/docs/current/static/sql-alterforefreignable.html) –
@joop verwenden, um diese 50k Datensätze abzurufen (die ungefähr 1 Datenstunde) dauert etwa 5-10 Minuten in einem Durchlauf. (mit 'SELECT tblA.propery_a, tblB.propery_b VON tblA JOIN tblB ON tblB.id_tblA = tblA.id WHERE tblB.batch_id = 'einige-uuid' UND tblA.some_fk = 'andere-uuid'') –