Vorwort oder wie die Idee der Partitionierung entstand
Der Beginn der Geschichte hier: Nachdem fast alle Ressourcen zur Optimierung der Abfrage zu diesem Zeitpunkt erschöpft waren, stellte sich die Frage – wie geht es weiter? So entstand die Idee der Partitionierung.

Ein lyrischer Einschub:
Gerade ‚zu diesem Zeitpunkt‘, denn . Danke und Habr!
Also, wie kann man den Kunden glücklich machen und gleichzeitig die eigenen Fähigkeiten verbessern?
Wenn man alles auf das Wesentliche reduziert, dann gibt es nur zwei radikale Wege, um die Leistung der Datenbank zu verbessern:
1) Extensiver Weg – Ressourcen erhöhen, Konfiguration ändern;
2) Intensiver Weg – Abfragen optimieren.
Da, wie gesagt, zu diesem Zeitpunkt bereits unklar war, was man an der Abfrage noch ändern könnte, um sie zu beschleunigen, wurde der Weg gewählt – die Gestaltung der Tabellen zu ändern.
Hier stellt sich die zentrale Frage – was und wie werden wir verändern?
Anfangsbedingungen
Zunächst gibt es dieses ERD (vereinfacht dargestellt):

Hauptmerkmale:
- Beziehungen ‚viele zu viele‘
- Die Tabelle hat bereits einen potenziellen Partitionierungsschlüssel.
Ursprüngliche Anfrage:
SELECT
p."PARAMETER_ID" as parameter_id,
pc."PC_NAME" AS pc_name,
pc."CUSTOMER_PARTNUMBER" AS customer_partnumber,
w."LASERMARK" AS lasermark,
w."LOTID" AS lotid,
w."REPORTED_VALUE" AS reported_value,
w."LOWER_SPEC_LIMIT" AS lower_spec_limit,
w."UPPER_SPEC_LIMIT" AS upper_spec_limit,
p."TYPE_CALCUL" AS type_calcul,
s."SHIPMENT_NAME" AS shipment_name,
s."SHIPMENT_DATE" AS shipment_date,
extract(year from s."SHIPMENT_DATE") AS year,
extract(month from s."SHIPMENT_DATE") as month,
s."REPORT_NAME" AS report_name,
p."SPARAM_NAME" AS SPARAM_name,
p."CUSTOMERPARAM_NAME" AS customerparam_name
FROM data w INNER JOIN shipment s ON s."SHIPMENT_ID" = w."SHIPMENT_ID"
INNER JOIN parameters p ON p."PARAMETER_ID" = w."PARAMETER_ID"
INNER JOIN shipment_pc sp ON s."SHIPMENT_ID" = sp."SHIPMENT_ID"
INNER JOIN pc pc ON pc."PC_ID" = sp."PC_ID"
INNER JOIN ( SELECT w2."LASERMARK" , MAX(s2."SHIPMENT_DATE") AS "SHIPMENT_DATE"
FROM shipment s2 INNER JOIN data w2 ON s2."SHIPMENT_ID" = w2."SHIPMENT_ID"
GROUP BY w2."LASERMARK"
) md ON md."SHIPMENT_DATE" = s."SHIPMENT_DATE" AND md."LASERMARK" = w."LASERMARK"
WHERE
s."SHIPMENT_DATE" >= '2018-07-01' AND s."SHIPMENT_DATE" <= '2018-09-30';
Ergebnisse der Ausführung auf der Testdatenbank:
Kosten : 502 997.55
Ausführungszeit: 505 Sekunden.
Was sehen wir? Eine gewöhnliche Anfrage in einem zeitlichen Schnitt.
Treffen wir eine einfache logische Annahme: Wenn es einen Zeitstich gibt, hilft uns das? Richtig – die Partitionierung.
Was partitionieren?
Auf den ersten Blick ist die Wahl offensichtlich – die deklarative Partitionierung der Tabelle „shipment“ nach dem Schlüssel „SHIPMENT_DATE“ (um es vorwegzunehmen – am Ende war es in der Produktion etwas anders).
Wie partitionieren?
Diese Frage ist ebenfalls nicht allzu kompliziert. Glücklicherweise gibt es in PostgreSQL 10 nun einen benutzerfreundlichen Partitionierungsmechanismus.
Also:
- Wir sichern das Dump der Ausgangstabelle – pg_dump source_table
- Wir löschen die Ausgangstabelle – drop table source_table
- Wir erstellen die übergeordnete Tabelle mit Partitionierung nach Bereich – create table source_table
- Wir erstellen die Partitionen – create table source_table, create index
- Wir importieren das Dump, das in Schritt 1 erstellt wurde – pg_restore
Skripte für die Partitionierung
Zur Vereinfachung und zum Komfort wurden die Schritte 2, 3 und 4 in einem Skript zusammengefasst.
Also:
Wir sichern das Dump der Ausgangstabelle
pg_dump postgres --file=/dump/shipment.dmp --format=c --table=shipment --verbose > /dump/shipment.log 2>&1Wir löschen die Ausgangstabelle + Wir erstellen die übergeordnete Tabelle mit Partitionierung nach Bereich + Wir erstellen die Partitionen
--create_partition_shipment.sql
do language plpgsql $$
declare
rec_shipment_date RECORD ;
partition_name varchar;
index_name varchar;
current_year varchar ;
current_month varchar ;
begin_year varchar ;
begin_month varchar ;
next_year varchar ;
next_month varchar ;
first_flag boolean ;
i integer ;
begin
RAISE NOTICE 'ERSTELLEN SIE EINE TEMPORÄRE TABELLE FÜR DAS VERSAND_DATUM';
CREATE TEMP TABLE tmp_shipment_date as select distinct "SHIPMENT_DATE" from shipment order by "SHIPMENT_DATE" ;
RAISE NOTICE 'TABELLE versand löschen';
drop table shipment cascade ;
CREATE TABLE public.shipment
(
"SHIPMENT_ID" integer NOT NULL DEFAULT nextval('shipment_shipment_id_seq'::regclass),
"SHIPMENT_NAME" character varying(30) COLLATE pg_catalog."default",
"SHIPMENT_DATE" timestamp without time zone,
"REPORT_NAME" character varying(40) COLLATE pg_catalog."default"
)
PARTITION BY RANGE ("SHIPMENT_DATE")
WITH (
OIDS = FALSE
)
TABLESPACE pg_default;
RAISE NOTICE 'ERSTELLEN SIE PARTITIONEN FÜR DIE TABELLE versand';
current_year:='0';
current_month:='0';
begin_year := '0' ;
begin_month := '0' ;
next_year := '0' ;
next_month := '0' ;
FOR rec_shipment_date IN SELECT * FROM tmp_shipment_date LOOP
RAISE NOTICE 'VERSAND_DATUM=%',rec_shipment_date."SHIPMENT_DATE";
current_year := date_part('year' ,rec_shipment_date."SHIPMENT_DATE");
current_month := date_part('month' ,rec_shipment_date."SHIPMENT_DATE") ;
IF to_number(current_month,'99') = to_date( begin_year||'.'||begin_month, 'YYYY.MM') AND
to_date( current_year||'.'||current_month, 'YYYY.MM') < to_date( next_year||'.'||next_month, 'YYYY.MM') AND
NOT first_flag
THEN
CONTINUE ;
ELSE
--NEUE Grenzen nur für das zweite und weitere Mal
begin_year := current_year ;
begin_month := current_month ;
IF current_month = '12' THEN
next_year := date_part('year' ,rec_shipment_date."SHIPMENT_DATE" + interval '1 year') ;
ELSE
next_year := current_year ;
END IF;
next_month := date_part('month' ,rec_shipment_date."SHIPMENT_DATE" + interval '1 month') ;
END IF;
partition_name := 'shipment_shipment_date_'||begin_year||'-'||begin_month||'-01-'|| next_year||'-'||next_month||'-01' ;
EXECUTE format('CREATE TABLE ' || quote_ident(partition_name) || ' PARTITION OF shipment FOR VALUES FROM ( %L ) TO ( %L ) ' , current_year||'-'||current_month||'-01' , next_year||'-'||next_month||'-01' ) ;
index_name := partition_name||'_shipment_id_idx';
RAISE NOTICE 'INDEX NAME =%',index_name;
EXECUTE format('CREATE INDEX ' || quote_ident(index_name) || ' ON '|| quote_ident(partition_name) ||' USING btree ("SHIPMENT_ID") TABLESPACE pg_default ' ) ;
--Erstes Mal Flag löschen
first_flag := false ;
END LOOP;
end
$$;Importieren Sie das Dump
pg_restore -d postgres --data-only --format=c --table=shipment --verbose shipment.dmp > /tmp/data_dump/shipment_restore.log 2>&1Überprüfen der Ergebnisse der Partitionierung
Was haben wir als Ergebnis? Der vollständige Text des Ausführungsplans ist lang und langweilig, daher können wir uns gut mit den Endzahlen begnügen.
War
Kosten: 502 997.55
Ausführungszeit: 505 Sekunden.
Ist
Kosten: 77 872.36
Ausführungszeit: 79 Sekunden.
Ein durchaus gutes Ergebnis. Wir haben sowohl die Kosten als auch die Ausführungszeit gesenkt. Insgesamt zeigt die Verwendung der Partitionierung den erwarteten Effekt und es gab keine Überraschungen.
Den Auftraggeber erfreuen
Die Testergebnisse wurden dem Auftraggeber zur Prüfung vorgelegt. Nach der Durchsicht erhielt er ein etwas unerwartetes Urteil: „Ausgezeichnet, partitionieren Sie die Tabelle 'data'.“
Ja, aber wir haben doch eine ganz andere Tabelle 'shipment' untersucht, die Tabelle 'data' hat kein Feld 'SHIPMENT_DATE'.
Kein Problem, fügen Sie hinzu, ändern Sie. Hauptsache der Auftraggeber ist mit dem Ergebnis zufrieden, die Details der Umsetzung sind nicht so wichtig.
Wir partitionieren die Haupttabelle 'data'
Im Grunde gab es keine besonderen Schwierigkeiten. Die Partitionierungsalgorithmus hat sich allerdings etwas geändert.
Fügen Sie die Spalte 'SHIPMENT_DATA' zur Tabelle 'data' hinzu
psql -h host -U database -d user
=> ALTER TABLE data ADD COLUMN "SHIPMENT_DATE" timestamp without time zone ;Wir füllen die Werte der Spalte „SHIPMENT_DATA“ in der Tabelle „data“ mit den entsprechenden Werten aus der Tabelle „shipment“.
-----------------------------
--update_data.sql
--Aktualisierung der veränderten Tabelle "data" mit Werten von "shipment_data" aus der Tabelle "shipment"
--Version 1.0
do language plpgsql $$
declare
rec_shipment_data RECORD ;
shipment_date timestamp without time zone ;
row_count integer ;
total_rows integer ;
begin
select count(*) into total_rows from shipment ;
RAISE NOTICE 'Insgesamt %',total_rows;
row_count:= 0 ;
FOR rec_shipment_data IN SELECT * FROM shipment LOOP
update data set "SHIPMENT_DATE" = rec_shipment_data."SHIPMENT_DATE" where "SHIPMENT_ID" = rec_shipment_data."SHIPMENT_ID";
row_count:= row_count +1 ;
RAISE NOTICE 'Anzahl der Zeilen = % , von %',row_count,total_rows;
END LOOP;
end
$$;Wir speichern den Dump der Tabelle „data“
pg_dump postgres --file=/dump/data.dmp --format=c --table=data --verbose > /dump/data.log 2>&1<sourceWir rekonstruieren die partitionierte Tabelle „data“
--create_partition_data.sql
--Partitionierung der Tabelle "Wafersdaten" nach dem Bereichsspalte "Versanddatum" mit einer Dauer von einem Monat
--Version 1.0
do language plpgsql $$
declare
rec_shipment_date RECORD ;
partition_name varchar;
index_name varchar;
current_year varchar ;
current_month varchar ;
begin_year varchar ;
begin_month varchar ;
next_year varchar ;
next_month varchar ;
first_flag boolean ;
i integer ;
begin
RAISE NOTICE 'TEMPORÄRE TABELLE FÜR VERSANDDATUM ERSTELLEN';
CREATE TEMP TABLE tmp_shipment_date as select distinct "SHIPMENT_DATE" from shipment order by "SHIPMENT_DATE" ;
RAISE NOTICE 'TABELLE DATA LÖSCHEN';
drop table data cascade ;
RAISE NOTICE 'PARTITIONIERTE TABELLE DATA ERSTELLEN';
CREATE TABLE public.data
(
"RUN_ID" integer,
"LASERMARK" character varying(20) COLLATE pg_catalog."default" NOT NULL,
"LOTID" character varying(80) COLLATE pg_catalog."default",
"SHIPMENT_ID" integer NOT NULL,
"PARAMETER_ID" integer NOT NULL,
"INTERNAL_VALUE" character varying(75) COLLATE pg_catalog."default",
"REPORTED_VALUE" character varying(75) COLLATE pg_catalog."default",
"LOWER_SPEC_LIMIT" numeric,
"UPPER_SPEC_LIMIT" numeric ,
"SHIPMENT_DATE" timestamp without time zone
)
PARTITION BY RANGE ("SHIPMENT_DATE")
WITH (
OIDS = FALSE
)
TABLESPACE pg_default ;
RAISE NOTICE 'PARTITIONEN FÜR DIE TABELLE DATA ERSTELLEN';
current_year:='0';
current_month:='0';
begin_year := '0' ;
begin_month := '0' ;
next_year := '0' ;
next_month := '0' ;
i := 1;
FOR rec_shipment_date IN SELECT * FROM tmp_shipment_date LOOP
RAISE NOTICE 'VERSANDDATUM=%',rec_shipment_date."SHIPMENT_DATE";
current_year := date_part('year' ,rec_shipment_date."SHIPMENT_DATE");
current_month := date_part('month' ,rec_shipment_date."SHIPMENT_DATE") ;
--Grenzen initialisieren
IF begin_year = '0' THEN
RAISE NOTICE '***Grenzen initialisieren';
first_flag := true ; --Erstmalige Flag
begin_year := current_year ;
begin_month := current_month ;
IF current_month = '12' THEN
next_year := date_part('year' ,rec_shipment_date."SHIPMENT_DATE" + interval '1 year') ;
ELSE
next_year := current_year ;
END IF;
next_month := date_part('month' ,rec_shipment_date."SHIPMENT_DATE" + interval '1 month') ;
END IF;
-- RAISE NOTICE 'current_year=% , current_month=% ',current_year,current_month;
-- RAISE NOTICE 'begin_year=% , begin_month=% ',begin_year,begin_month;
-- RAISE NOTICE 'next_year=% , next_month=% ',next_year,next_month;
-- Aktuelles Datum in die Grenzen prüfen NICHT beim ersten Mal
RAISE NOTICE 'Aktuelles Datum = %',to_char( to_date( current_year||'.'||current_month, 'YYYY.MM'), 'YYYY.MM');
RAISE NOTICE 'BEGINN DATUM = %',to_char( to_date( begin_year||'.'||begin_month, 'YYYY.MM'), 'YYYY.MM');
RAISE NOTICE 'NÄCHSTES DATUM = %',to_char( to_date( next_year||'.'||next_month, 'YYYY.MM'), 'YYYY.MM');
IF to_date( current_year||'.'||current_month, 'YYYY.MM') >= to_date( begin_year||'.'||begin_month, 'YYYY.MM') AND
to_date( current_year||'.'||current_month, 'YYYY.MM') < to_date( next_year||'.'||next_month, 'YYYY.MM') AND
NOT first_flag
THEN
RAISE NOTICE '***FORTSETZEN';
CONTINUE ;
ELSE
--NEUE Grenzen nur beim zweiten und weiteren Mal
RAISE NOTICE '***NEUE GRENZEN';
begin_year := current_year ;
begin_month := current_month ;
IF current_month = '12' THEN
next_year := date_part('year' ,rec_shipment_date."SHIPMENT_DATE" + interval '1 year') ;
ELSE
next_year := current_year ;
END IF;
next_month := date_part('month' ,rec_shipment_date."SHIPMENT_DATE" + interval '1 month') ;
END IF;
IF to_number(current_month,'99') < 10 THEN
current_month := '0'||current_month ;
END IF ;
IF to_number(begin_month,'99') < 10 THEN
begin_month := '0'||begin_month ;
END IF ;
IF to_number(next_month,'99') < 10 THEN
next_month := '0'||next_month ;
END IF ;
RAISE NOTICE 'current_year=% , current_month=% ',current_year,current_month;
RAISE NOTICE 'begin_year=% , begin_month=% ',begin_year,begin_month;
RAISE NOTICE 'next_year=% , next_month=% ',next_year,next_month;
partition_name := 'data_'||begin_year||begin_month||'01_'||next_year||next_month||'01' ;
RAISE NOTICE 'PARTITION NUMMER % , TABELLENNAME =%',i , partition_name;
EXECUTE format('CREATE TABLE ' || quote_ident(partition_name) || ' PARTITION OF data FOR VALUES FROM ( %L ) TO ( %L ) ' , begin_year||'-'||begin_month||'-01' , next_year||'-'||next_month||'-01' ) ;
index_name := partition_name||'_shipment_id_parameter_id_idx';
RAISE NOTICE 'INDEXNAME =%',index_name;
EXECUTE format('CREATE INDEX ' || quote_ident(index_name) || ' ON '|| quote_ident(partition_name) ||' USING btree ("SHIPMENT_ID", "PARAMETER_ID") TABLESPACE pg_default ' ) ;
index_name := partition_name||'_lasermark_idx';
RAISE NOTICE 'INDEXNAME =%',index_name;
EXECUTE format('CREATE INDEX ' || quote_ident(index_name) || ' ON '|| quote_ident(partition_name) ||' USING btree ("LASERMARK" COLLATE pg_catalog."default") TABLESPACE pg_default ' ) ;
index_name := partition_name||'_shipment_id_idx';
RAISE NOTICE 'INDEXNAME =%',index_name;
EXECUTE format('CREATE INDEX ' || quote_ident(index_name) || ' ON '|| quote_ident(partition_name) ||' USING btree ("SHIPMENT_ID") TABLESPACE pg_default ' ) ;
index_name := partition_name||'_parameter_id_idx';
RAISE NOTICE 'INDEXNAME =%',index_name;
EXECUTE format('CREATE INDEX ' || quote_ident(index_name) || ' ON '|| quote_ident(partition_name) ||' USING btree ("PARAMETER_ID") TABLESPACE pg_default ' ) ;
index_name := partition_name||'_shipment_date_idx';
RAISE NOTICE 'INDEXNAME =%',index_name;
EXECUTE format('CREATE INDEX ' || quote_ident(index_name) || ' ON '|| quote_ident(partition_name) ||' USING btree ("SHIPMENT_DATE") TABLESPACE pg_default ' ) ;
--Erste Flag löschen
first_flag := false ;
END LOOP;
end
$$;
Laden Sie das im Schritt 3 erstellte Dump-Backup hoch.
pg_restore -h host -u user -d database --data-only --format=c --table=data --verbose data.dmp > data_restore.log 2>&1Erstellen Sie einen separaten Abschnitt für alte Daten.
---------------------------------------------------
--create_partition_for_old_dates.sql
--Partitionen für die Speicherung alter Datumswerte erstellen
--Version 1.0
do language plpgsql $$
declare
rec_shipment_date RECORD;
partition_name varchar;
index_name varchar;
begin
SELECT min("SHIPMENT_DATE") AS min_date INTO rec_shipment_date from data;
RAISE NOTICE 'Altes Datum ist %', rec_shipment_date.min_date;
partition_name := 'data_old_dates';
RAISE NOTICE 'PARTITIONNAME IST %', partition_name;
EXECUTE format('CREATE TABLE ' || quote_ident(partition_name) || ' PARTITION OF data FOR VALUES FROM ( %L ) TO ( %L )', '1900-01-01',
to_char(rec_shipment_date.min_date, 'YYYY') || '-' || to_char(rec_shipment_date.min_date, 'MM') || '-01');
index_name := partition_name || '_shipment_id_parameter_id_idx';
EXECUTE format('CREATE INDEX ' || quote_ident(index_name) || ' ON ' || quote_ident(partition_name) || ' USING btree ("SHIPMENT_ID", "PARAMETER_ID") TABLESPACE pg_default');
index_name := partition_name || '_lasermark_idx';
EXECUTE format('CREATE INDEX ' || quote_ident(index_name) || ' ON ' || quote_ident(partition_name) || ' USING btree ("LASERMARK" COLLATE pg_catalog."default") TABLESPACE pg_default');
index_name := partition_name || '_shipment_id_idx';
EXECUTE format('CREATE INDEX ' || quote_ident(index_name) || ' ON ' || quote_ident(partition_name) || ' USING btree ("SHIPMENT_ID") TABLESPACE pg_default');
index_name := partition_name || '_parameter_id_idx';
EXECUTE format('CREATE INDEX ' || quote_ident(index_name) || ' ON ' || quote_ident(partition_name) || ' USING btree ("PARAMETER_ID") TABLESPACE pg_default');
index_name := partition_name || '_shipment_date_idx';
EXECUTE format('CREATE INDEX ' || quote_ident(index_name) || ' ON ' || quote_ident(partition_name) || ' USING btree ("SHIPMENT_DATE") TABLESPACE pg_default');
end
$$;Endergebnisse:
War
Kosten: 502 997.55
Ausführungszeit: 505 Sekunden.
Ist
Kosten: 68 533.70
Ausführungszeit: 69 Sekunden
Eindrucksvoll, wirklich eindrucksvoll. Und wenn man bedenkt, dass ich auf dem Weg das Paginierungssystem in PostgreSQL 10 einigermaßen beherrscht habe – ein hervorragendes Ergebnis.
Abschweifer
Kann man es noch besser machen – JA, DAS KANN MAN!Dafür muss man MATERIALIZED VIEW verwenden.
CREATE MATERIALIZED VIEW LASERMARK_VIEW
CREATE MATERIALIZED VIEW LASERMARK_VIEW
AS
SELECT w."LASERMARK", MAX(s."SHIPMENT_DATE") AS "SHIPMENT_DATE"
FROM shipment s INNER JOIN data w ON s."SHIPMENT_ID" = w."SHIPMENT_ID"
GROUP BY w."LASERMARK";
CREATE INDEX lasermark_vw_shipment_date_ind ON lasermark_view USING btree ("SHIPMENT_DATE") TABLESPACE pg_default;
analyze lasermark_view;
Ein weiteres Mal überarbeiten wir die Abfrage:
Abfrage unter Verwendung des materialized view
WÄHLEN
p."PARAMETER_ID" AS parameter_id,
pc."PC_NAME" AS pc_name,
pc."CUSTOMER_PARTNUMBER" AS customer_partnumber,
w."LASERMARK" AS lasermark,
w."LOTID" AS lotid,
w."REPORTED_VALUE" AS reported_value,
w."LOWER_SPEC_LIMIT" AS lower_spec_limit,
w."UPPER_SPEC_LIMIT" AS upper_spec_limit,
p."TYPE_CALCUL" AS type_calcul,
s."SHIPMENT_NAME" AS shipment_name,
s."SHIPMENT_DATE" AS shipment_date,
extract(year from s."SHIPMENT_DATE") AS year,
extract(month from s."SHIPMENT_DATE") AS month,
s."REPORT_NAME" AS report_name,
p."STC_NAME" AS STC_name,
p."CUSTOMERPARAM_NAME" AS customerparam_name
FROM data w INNER JOIN shipment s ON s."SHIPMENT_ID" = w."SHIPMENT_ID"
INNER JOIN parameters p ON p."PARAMETER_ID" = w."PARAMETER_ID"
INNER JOIN shipment_pc sp ON s."SHIPMENT_ID" = sp."SHIPMENT_ID"
INNER JOIN pc pc ON pc."PC_ID" = sp."PC_ID"
INNER JOIN LASERMARK_VIEW md ON md."SHIPMENT_DATE" = s."SHIPMENT_DATE" AND md."LASERMARK" = w."LASERMARK"
WHERE
s."SHIPMENT_DATE" >= '2018-07-01' AND s."SHIPMENT_DATE" <= '2018-09-30';
Und wir erhalten ein weiteres Ergebnis:
War
Kosten: 502 997.55
Ausführungszeit: 505 Sekunden
Ist
Kosten: 42 481.16
Ausführungszeit: 43 Sekunden.
Obwohl natürlich, ein so vielversprechendes Ergebnis täuscht, Präsentationen müssen aktualisiert werden. Daher wird die endgültige Datenabrufzeit nicht wirklich helfen. Aber als Experiment ist es durchaus interessant.
Tatsächlich, wie sich herausstellte, danke nochmal und Habr !-
Nachwort
Also, der Kunde ist zufrieden. Und man muss die Situation nutzen.
Neue Aufgabe: Was könnte man sich einfallen lassen, um tiefere Einblicke zu gewinnen und den Horizont zu erweitern?
Und da fällt mir ein – Leute, wir haben kein Monitoring für unsere PostgreSQL-Datenbanken.
Um ehrlich zu sein, es gibt zwar eine Art Monitoring wie Cloud Watch bei AWS, aber welchen Nutzen hat dieses Monitoring für einen DBA? Letztendlich praktisch keinen.
Wenn sich die Gelegenheit bietet, etwas Nützliches und Interessantes für sich selbst zu tun, sollte man diese Chance unbedingt nutzen …
DENN

So sind wir also zum spannendsten Teil gekommen:
3. Dezember 2018.
Die Entscheidung, mit der Untersuchung der verfügbaren Möglichkeiten zur Performance-Überwachung von PostgreSQL-Queries zu beginnen.
Aber das ist schon eine ganz andere Geschichte.
Fortsetzung folgt …
Quelle: habr.com
