PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Optimieren von Input/Output (I/O)-Operationen für PostgreSQL

PostgreSQL ist eine der beliebtesten Open-Source-Datenbanken der Welt und wurde erfolgreich in mehreren geschäftskritischen Umgebungen in verschiedenen Domänen implementiert, wobei High-End-OLTP-Anwendungen in Echtzeit verwendet werden, die Millionen und Milliarden von Transaktionen pro Tag ausführen. PostgreSQL I/O ist ziemlich zuverlässig, stabil und leistungsfähig auf so ziemlich jeder Hardware, sogar in der Cloud.

Um sicherzustellen, dass Datenbanken im erwarteten Umfang mit den erwarteten Reaktionszeiten funktionieren, ist ein gewisses Leistungs-Engineering erforderlich. Nun, das Erreichen einer guten Datenbankleistung hängt von verschiedenen Faktoren ab. Die Datenbankleistung kann aus verschiedenen Gründen nachlassen, z. B. Infrastrukturdimensionierung, ineffiziente Datenbankwartungsstrategie, schlechter SQL-Code oder schlecht konfigurierte Datenbankprozesse, die nicht alle verfügbaren Ressourcen nutzen – CPU, Arbeitsspeicher, Netzwerkbandbreite und Festplatten-E/A.

Was kann dazu führen, dass sich die Datenbankleistung verschlechtert?

  • Schlecht geschriebene Abfragen mit fehlerhaften Verknüpfungen, Logik usw., die viel CPU und Speicher beanspruchen
  • Abfragen, die Full-Table-Scans für große Tabellen aufgrund unsachgemäßer Indizierung durchführen
  • Schlechte Datenbankwartung ohne ordnungsgemäße Statistiken
  • Ineffiziente Kapazitätsplanung, die zu unzureichend dimensionierter Infrastruktur führt
  • Unsachgemäßes logisches und physisches Design
  • Kein Verbindungspooling vorhanden, was dazu führt, dass Anwendungen auf unkontrollierbare Weise eine große Anzahl von Verbindungen herstellen

Das sind also viele potenzielle Bereiche, die Leistungsprobleme verursachen können. Einer der wichtigen Bereiche, auf die ich mich in diesem Blog konzentrieren möchte, ist die Optimierung der E/A-Leistung (Eingabe/Ausgabe) von PostgreSQL. Das Optimieren der Input/Output-Operationen von PostgreSQL ist von wesentlicher Bedeutung, insbesondere in einer Umgebung mit vielen Transaktionen wie OLTP oder in einer Data-Warehousing-Umgebung mit komplexer Datenanalyse bei riesigen Datensätzen.

In den meisten Fällen werden Datenbankleistungsprobleme hauptsächlich durch hohe E/A verursacht. Das bedeutet, dass Datenbankprozesse mehr Zeit damit verbringen, entweder auf die Festplatte zu schreiben oder von ihr zu lesen. Jede Echtzeit-Datenoperation ist E/A-gebunden, es ist zwingend erforderlich sicherzustellen, dass die Datenbank E/A-optimiert ist. In diesem Blog werde ich mich auf allgemeine E/A-Probleme konzentrieren, auf die PostgreSQL-Datenbanken in Echtzeit-Produktionsumgebungen stoßen können.

PostgreSQL-I/O optimieren

Das Optimieren der PostgreSQL-E/A ist für den Aufbau einer hochleistungsfähigen und skalierbaren Datenbankarchitektur unerlässlich. Sehen wir uns verschiedene Faktoren an, die sich auf die E/A-Leistung auswirken:

  • Indizierung
  • Partitionierung
  • Kontrollpunkte
  • VAKUUM, ANALYSE (mit FÜLLFAKTOR)
  • Andere E/A-Probleme
  • PostgreSQL-E/A in der Cloud
  • Werkzeuge

Indizierung

Die Indizierung ist eine der zentralen Optimierungstechniken, die eine entscheidende Rolle bei der Verbesserung der Datenbank-E/A-Leistung spielt. Dies gilt wirklich für jede Datenbank. PostgreSQL unterstützt verschiedene Indextypen, die Lesevorgänge erheblich beschleunigen können, was zu einer verbesserten Skalierbarkeit für Anwendungen führt. Während das Erstellen von Indizes ziemlich einfach und unkompliziert ist, ist es für DBAs und Entwickler unerlässlich, zu wissen, welche Art von Index zu wählen ist und welche Spalten verwendet werden. Letzteres basiert auf verschiedenen Faktoren wie Abfragekomplexität, Datentyp, Datenkardinalität, Schreibvolumen, Datengröße, Festplattenarchitektur, Infrastruktur (öffentliche Cloud, private Cloud oder lokal) usw.

Während die Indizierung die Leseleistung von Abfragen erheblich verbessern kann, kann sie auch die Schreibvorgänge verlangsamen, die auf die indizierten Spalten treffen. Sehen wir uns ein Beispiel an:

Einfluss von Indizes auf READ-Operationen

Eine Tabelle namens emp mit etwa 1 Million Zeilen.

READ-Leistung ohne Index

postgres=# select * from emp where eid=10;

 eid | ename    | peid | did  |    doj
-----+---------------+--------+------+------------
  10 | emp        |          |   1   | 2018-06-06
(1 row)
 
Time: 70.020 ms => took about 70+ milli-seconds to respond with on row

READ-Leistung mit einem Index

Lassen Sie uns einen Index auf die eid-Spalte setzen und den Unterschied sehen

postgres=# create index indx001 on emp ( eid );
CREATE INDEX

postgres=# select * from emp where eid=10;

 eid | ename  | peid | did |    doj
------+-------------+-------+------+------------
  10 | emp      |          |   1   | 2018-06-06
(1 row)
 
Time: 0.454 ms =>  0.4+ milli-seconds!!! thats a huge difference - isn’t it?

Indexierung ist also wichtig.

Einfluss von Indizes auf WRITE-Operationen

Indizes verlangsamen die Schreibleistung. Während die Indizes einen Einfluss auf alle Arten von Schreibvorgängen haben, wollen wir uns einige Analysen zu den Auswirkungen von Indizes auf INSERTs ansehen

Einfügen von 1 Million Zeilen in eine Tabelle ohne Indizes

postgres=# do $$
postgres$# declare
postgres$# i integer;
postgres$# begin
postgres$# for i in 1..1000000 loop
postgres$# insert into emp values (i,'emp',null,1,current_date);
postgres$# end loop;
postgres$# end $$;
DO

Time: 4818.470 ms (00:04.818) => Takes about 4.8 seconds

Einfügen derselben 1 Million Zeilen mit einem Index

Lassen Sie uns zuerst einen Index erstellen

postgres=# create index indx001 on emp ( eid );
CREATE INDEX

postgres=# do $$
postgres$# declare
postgres$# i integer;
postgres$# begin
postgres$# for i in 1..1000000 loop
postgres$# insert into emp values (i,'emp',null,1,current_date);
postgres$# end loop;
postgres$# end $$;
DO

Time: 7825.494 ms (00:07.825) =>  Takes about 7.8 seconds

Wie wir also beobachten können, hat sich die INSERT-Zeit mit nur einem Index um 80 % erhöht und kann viel mehr Zeit in Anspruch nehmen, wenn mehrere Indizes vorhanden sind. Es kann noch schlimmer werden, wenn es funktionsbasierte Indizes gibt. Damit müssen DBAs leben! Indizes erhöhen die Schreibleistung. Es gibt jedoch Möglichkeiten, dieses Problem anzugehen, das von der Festplattenarchitektur abhängt. Wenn der Datenbankserver mehrere Plattendateisysteme verwendet, können die Indizes und Tabellen über mehrere Tablespaces verteilt werden, die sich über mehrere Plattendateisysteme befinden. Auf diese Weise kann eine bessere I/O-Performance erreicht werden.

TIPPS zur Indexverwaltung

  • Verstehen Sie die Notwendigkeit von Indizes. Intelligente Indizierung ist der Schlüssel.
  • Vermeiden Sie das Erstellen mehrerer Indizes und auf keinen Fall unnötige Indizes, da dies die Schreibleistung wirklich beeinträchtigen kann.
  • Überwachen Sie die Nutzung von Indizes und löschen Sie ungenutzte Indizes.
  • Wenn indizierte Spalten Datenänderungen unterliegen, werden auch Indizes aufgebläht. Organisieren Sie die Indizes daher regelmäßig neu.

Partitionierung

Eine effektive Partitionierungsstrategie kann E/A-Leistungsprobleme erheblich reduzieren. Große Tabellen können basierend auf der Geschäftslogik partitioniert werden. PostgreSQL unterstützt die Tabellenpartitionierung. Obwohl es derzeit nicht alle Funktionen vollständig unterstützt, kann es nur bei einigen Echtzeit-Anwendungsfällen helfen. In PostgreSQL sind partitionierte untergeordnete Tabellen vollständig individuell für die Haupttabelle, was einen Engpass darstellt. Beispielsweise können Einschränkungen, die für die Haupttabelle erstellt wurden, nicht automatisch an die untergeordneten Tabellen vererbt werden.

Aus der Perspektive des E/A-Ausgleichs kann die Partitionierung jedoch wirklich hilfreich sein. Alle untergeordneten Partitionen können auf mehrere Tablespaces und Plattendateisysteme aufgeteilt werden. Abfragen mit einem Datumsbereich in der „Where“-Klausel, die auf die Tabelle treffen, die basierend auf dem Datumsbereich partitioniert ist, können von der Partitionierung profitieren, indem nur eine oder zwei Partitionen anstelle der vollständigen Tabelle gescannt werden.

Checkpointing

Prüfpunkte definieren den konsistenten Zustand der Datenbank. Sie sind kritisch und es ist wichtig, dass Checkpoints regelmäßig genug auftreten, um sicherzustellen, dass Datenänderungen dauerhaft auf der Festplatte gespeichert werden und die Datenbank die ganze Zeit in einem konsistenten Zustand ist. Allerdings kann eine unsachgemäße Konfiguration von Prüfpunkten zu Problemen mit der E/A-Leistung führen. DBAs müssen bei der Konfiguration von Checkpoints sorgfältig vorgehen, um sicherzustellen, dass keine E/A-Spitzen auftreten, und dies hängt auch davon ab, wie gut die Festplatten sind und wie gut das Layout der Datendateien aufgebaut ist.

Welcher Checkpoint macht ?

In einfachen Worten, Checkpoints sorgen für:

  • Alle festgeschriebenen Daten werden in die Datendateien auf der Festplatte geschrieben.
  • Clog-Dateien werden mit dem Commit-Status aktualisiert.
  • Transaktionsprotokolldateien im Verzeichnis pg_xlog (jetzt pg_wal) werden recycelt.

Das erklärt, wie I/O-intensive Checkpoints sind. Es gibt Parameter in postgresql.conf, die konfiguriert/abgestimmt werden können, um das Checkpoint-Verhalten zu steuern, und diese Parameter sind max_wal_size, min_wal_size, checkpoint_timeout und checkpoint_completion_target. Diese Parameter entscheiden, wie oft die Checkpoints auftreten sollen und innerhalb welcher Zeit die Checkpoints beendet werden müssen.

Wie kann man verstehen, welche Konfiguration für Checkpoints besser ist? Wie werden sie eingestellt?

Hier sind einige Tipps:

  • Evaluieren Sie den Datenbank-TPS. Bewerten Sie das Gesamtvolumen der Transaktionen, die an einem Geschäftstag in der Datenbank stattfinden, und identifizieren Sie auch, zu welcher Zeit die höchste Anzahl von Transaktionen in der Datenbank eintrifft.
  • Diskutieren Sie regelmäßig mit Anwendungsentwicklern und anderen technischen Teams, um die Statistik der Datenbanktransaktionen sowie das zukünftige Transaktionswachstum zu verstehen.
  • Dies kann auch von der Datenbankseite aus erfolgen:
    • Überwachen Sie die Datenbank und werten Sie die Anzahl der Transaktionen aus, die während des Tages stattfinden. Dies kann durch Abfragen von pgcatalog-Tabellen wie pg_stat_user_tables.

      erfolgen
    • Bewerten Sie die Anzahl der pro Tag generierten wal-Archivdateien

    • Überwachen Sie die Leistung der Checkpoints, indem Sie den Parameter log_checkpoints

      aktivieren
      2018-06-06 15:03:16.446 IST [2111] LOG:  checkpoint starting: xlog
      2018-06-06 15:03:22.734 IST [2111] LOG:  checkpoint complete: wrote 12112 buffers (73.9%); 0 WAL file(s) added, 0 removed, 25 recycled; write=6.058 s, sync=0.218 s, total=6.287 s; sync files=4, longest=0.178 s, average=0.054 s; distance=409706 kB, estimate=412479 kB
    • Verstehen Sie, ob die aktuelle Prüfpunktkonfiguration für die Datenbank gut genug ist. Konfigurieren Sie den Parameter checkpoint_warning (standardmäßig auf 30 Sekunden konfiguriert), um die folgenden Warnungen in den Postgres-Protokolldateien anzuzeigen.

      2018-06-06 15:02:42.295 IST [2111] LOG:  checkpoints are occurring too frequently (11 seconds apart)
      2018-06-06 15:02:42.295 IST [2111] HINT:  Consider increasing the configuration parameter "max_wal_size".

Was bedeutet die obige Warnung?

Checkpoints treten im Allgemeinen immer dann auf, wenn Protokolldateien im Wert von max_wal_size (standardmäßig 1 GB, was 64 WAL-Dateien bedeutet) aufgefüllt sind oder wenn checkpoint_timeout (alle 5 Minuten standardmäßig) erreicht ist. Die obige Warnung bedeutet, dass die konfigurierte max_wal_size nicht angemessen ist und die Prüfpunkte alle 11 Sekunden auftreten, was wiederum bedeutet, dass 64 WAL-Dateien im PG_WAL-Verzeichnis in nur 11 Sekunden aufgefüllt werden, was zu häufig ist. Mit anderen Worten, wenn es weniger häufige Transaktionen gibt, werden die Kontrollpunkte alle 5 Minuten stattfinden. Erhöhen Sie also, wie der Hinweis schon sagt, den Parameter max_wal_size auf einen höheren Wert, der Parameter max_min_size kann auf denselben oder einen kleineren Wert als der vorherige erhöht werden.

Ein weiterer kritischer Parameter, der aus Sicht der E/A-Leistung zu berücksichtigen ist, ist checkpoint_completion_target, das standardmäßig auf 0,5 konfiguriert ist.

checkpoint_completion_target =0,5 x checkpoint_timeout =2,5 Minuten

Das bedeutet, dass Checkpoints 2,5 Minuten Zeit haben, um die schmutzigen Blöcke mit der Festplatte zu synchronisieren. Reichen 2,5 Minuten? Das muss evaluiert werden. Wenn die Anzahl der zu schreibenden Dirty-Blöcke sehr hoch ist, können 2,5 Minuten sehr, sehr aggressiv erscheinen, und dann kann eine E/A-Spitze beobachtet werden. Die Konfiguration des Parameters „completion_target“ muss basierend auf den Werten „max_wal_size“ und „checkpoint_timeout“ erfolgen. Wenn diese Parameter auf einen höheren Wert angehoben werden, erwägen Sie, checkpoint_completion_target entsprechend zu erhöhen.

VAKUUM, ANALYSE (mit FÜLLFAKTOR)

VACUUM ist eine der leistungsstärksten Funktionen von PostgreSQL. Es kann verwendet werden, um Aufblähungen (fragmentierter Speicherplatz) in Tabellen und Indizes zu entfernen, und wird von Transaktionen generiert. Die Datenbank muss regelmäßig VAKUUMiert werden, um eine gesunde Wartung und bessere Leistung sicherzustellen. Auch hier kann ein nicht regelmäßiges VAKUUMIEREN der Datenbank zu ernsthaften Leistungsproblemen führen. ANALYZE muss zusammen mit VACUUM (VACUUM ANALYZE) ausgeführt werden, um aktuelle Statistiken für den Abfrageplaner sicherzustellen.

Die VAKUUMANALYSE kann auf zwei Arten durchgeführt werden:manuell, automatisch oder beides. In einer Echtzeit-Produktionsumgebung ist es im Allgemeinen beides. Das automatische VAKUUM wird durch den Parameter „autovacuum“ aktiviert, der standardmäßig auf „on“ konfiguriert ist. Wenn die automatische Bereinigung aktiviert ist, beginnt PostgreSQL regelmäßig automatisch mit dem VAKUUMIEREN der Tabellen. Die Kandidatentabellen, die evakuiert werden müssen, werden von Autovacuum-Prozessen basierend auf verschiedenen Schwellenwerten aufgenommen, die durch verschiedene Autovacuum*-Parameter festgelegt werden. Diese Parameter können optimiert / abgestimmt werden, um sicherzustellen, dass Aufblähungen der Tabellen regelmäßig gelöscht werden. Sehen wir uns einige Parameter und ihre Verwendung an -

Autovakuumparameter

autovacuum=on Dieser Parameter wird verwendet, um das Autovakuum zu aktivieren/deaktivieren. Standard ist „ein“.
log_autovacuum_min_duration =-1 Protokolliert die Dauer des Autovacuum-Vorgangs. Dies ist wichtig, um zu verstehen, wie lange der Autovacuum-Prozess ausgeführt wurde.
autovacuum_max_workers =3 Anzahl der benötigten Autovacuum-Prozesse. Dies hängt davon ab, wie aggressiv Datenbanktransaktionen sind und wie viele CPUs Sie für Autovacuum-Prozesse anbieten können.
autovacuum_naptime =1 Minute Autovakuum-Ruhezeit zwischen Autovakuumläufen.

Parameter, die den Schwellenwert für den Start des Autovacuum-Prozesses definieren

Autovacuum-Jobs werden gestartet, wenn ein bestimmter Schwellenwert erreicht ist. Nachfolgend sind die Parameter aufgeführt, mit denen ein bestimmter Schwellenwert festgelegt werden kann, auf dessen Grundlage der Autovakuumprozess gestartet wird.

autovacuum_vacuum_threshold =50 Die Tabelle wird geleert, wenn mindestens 50 Zeilen in einer Tabelle aktualisiert/gelöscht werden.
autovacuum_analyze_threshold =50 Die Tabelle wird analysiert, wenn mindestens 50 Zeilen in einer Tabelle aktualisiert/gelöscht werden.
autovacuum_vacuum_scale_factor =0,2 Die Tabelle wird geleert, wenn mindestens 20 % der Zeilen in einer Tabelle aktualisiert/gelöscht werden.
autovacuum_analyze_scale_factor =0,1 Die Tabelle wird geleert, wenn mindestens 10 % der Zeilen in einer Tabelle aktualisiert/gelöscht werden.

Über dem Schwellenwert liegende Parameter können basierend auf dem Datenbankverhalten geändert werden. DBAs müssen die Hot Tables analysieren und identifizieren und sicherstellen, dass diese Tabellen so oft wie möglich bereinigt werden, um eine gute Leistung sicherzustellen. Das Erreichen eines bestimmten Werts für diese Parameter könnte in einer Umgebung mit vielen Transaktionen, in der jede Sekunde Datenänderungen stattfinden, eine Herausforderung darstellen. Oft ist mir aufgefallen, dass Selbstbereinigungsprozesse ziemlich lange dauern und am Ende zu viele Ressourcen in Produktionssystemen verbrauchen.

Ich würde vorschlagen, sich nicht vollständig auf den Autovacuum-Prozess zu verlassen. Der beste Weg ist, einen nächtlichen VACUUM ANALYZE-Job zu planen, damit die Belastung des Autovacuum verringert wird. Erwägen Sie zunächst, große Tabellen mit einer hohen Transaktionsrate manuell zu VAKUUMIEREN.

VAKUUM VOLL

VACUUM FULL hilft, den aufgeblähten Speicherplatz in den Tabellen und Indizes zurückzugewinnen. Dieses Dienstprogramm kann nicht verwendet werden, wenn die Datenbank online ist, da es die Tabelle sperrt. Tische dürfen nur dann VACUUM FULL unterzogen werden, wenn die Anwendungen heruntergefahren sind. Indizes werden zusammen mit Tabellen während VACUUM FULL ebenfalls reorganisiert.

Werfen wir einen Blick auf die Wirkung von VACUUM ANALYZE

Blähungen:Wie erkennt man Blähungen? Wann entstehen Aufblähungen?

Hier sind einige Tests:

Ich habe eine Tabelle der Größe 1 GB mit 10 Millionen Zeilen.

postgres=# select pg_relation_size('pgbench_accounts')/1024/1024/1024;

 ?column? 
----------------
        1

postgres=# select count(*) From pgbench_accounts ;
  count   
-----------------
 10000000

Sehen wir uns die Auswirkungen von Aufblähungen auf eine einfache Abfrage an:select * from pgbench_accounts;

Unten ist der Erklärungsplan für die Abfrage:

postgres=# explain analyze select * from pgbench_accounts;

QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------------
 Seq Scan on pgbench_accounts  (cost=0.00..263935.00 rows=10000000 width=97) 
 (actual time=0.033..1054.257 rows=10000000 loops=1)
 Planning time: 0.255 ms
 Execution time: 1494.448 ms

Lassen Sie uns nun alle Zeilen in der Tabelle aktualisieren und die Auswirkungen der obigen SELECT-Abfrage sehen.

postgres=# update pgbench_accounts set abalance=1;
UPDATE 10000000

postgres=# select count(*) From pgbench_accounts ;
  count   
-----------------
 10000000

Nachfolgend finden Sie den EXPLAIN PLAN der Abfrage nach der UPDATE-Ausführung.

postgres=# explain analyze select * from pgbench_accounts;

QUERY PLAN                                                             
----------------------------------------------------------------------------------------------------------------------------------------------------------
 Seq Scan on pgbench_accounts  (cost=0.00..527868.39 rows=19999939 width=97) 
 (actual time=404.474..1520.175 rows=10000000 loops=1)
 Planning time: 0.051 ms
 Execution time: 1958.532 ms

Die Größe der Tabelle hat sich nach dem UPDATE auf 2 GB erhöht

postgres=# select pg_relation_size('pgbench_accounts')/1024/1024/1024;

 ?column? 
-----------------
        2

Wenn Sie die Kostenzahlen des früheren EXPLAIN PLANs beobachten und vergleichen können, gibt es einen großen Unterschied. Die Kosten sind um ein Vielfaches gestiegen. Noch wichtiger ist, wenn Sie genau beobachten, dass die Anzahl der Zeilen (knapp über 19 Millionen), die nach dem UPDATE gescannt werden, höher ist, was fast das Doppelte der tatsächlich vorhandenen Zeilen (10 Millionen) ist. Das bedeutet, dass die Anzahl der aufgeblähten Zeilen 9+ Millionen beträgt und die tatsächliche Zeit ebenfalls gestiegen ist und die Ausführungszeit von 1,4 Sekunden auf 1,9 Sekunden gestiegen ist.

Das ist also die Auswirkung, wenn die TABELLE nach dem UPDATE nicht VAKUUMiert wird. Die obigen EXPLAIN PLAN-Zahlen bedeuten genau, dass die Tabelle aufgebläht ist.

Wie erkennt man, ob die Tabelle aufgebläht ist? Verwenden Sie pgstattuple contrib module:

postgres=# select * from pgstattuple('pgbench_accounts');
 table_len  | tuple_count | tuple_len  | tuple_percent | dead_tuple_count | dead_tuple_len | dead_tuple_percent | free_space | free_percent 
------------+-------------+------------+---------------+------------------+----------------+--------------------+------------+--------------
 2685902848 |    10000000 | 1210000000 |         45.05 |          9879891 |     1195466811 |              44.51 |   52096468 |         1.94

Die obige Zahl zeigt an, dass die Hälfte der Tabelle aufgebläht ist.

Lassen Sie uns die Tabelle VAKUUM ANALYSEN und sehen Sie sich jetzt die Auswirkungen an:

postgres=# VACUUM ANALYZE pgbench_accounts ;
VACUUM

postgres=# explain analyze select * from pgbench_accounts;

QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------------
 Seq Scan on pgbench_accounts  (cost=0.00..428189.05 rows=10032005 width=97) 
 (actual time=400.023..1472.118 rows=10000000 loops=1)
 Planning time: 4.374 ms
 Execution time: 1913.541 ms

Nach VACUUM ANALYZE sind die Kostenzahlen gesunken. Jetzt zeigt die Anzahl der gescannten Zeilen fast 10 Millionen an, auch die tatsächliche Zeit und die Ausführungszeit haben sich nicht wesentlich geändert. Denn obwohl die Aufblähungen in der Tabelle verschwunden sind, bleibt die Größe der zu scannenden Tabelle gleich. Unten ist die pgstattuple Ausgabe nach VACUUM ANALYZE.

postgres=# select * from pgstattuple('pgbench_accounts');

 table_len  | tuple_count | tuple_len  | tuple_percent | dead_tuple_count | dead_tuple_len | dead_tuple_percent | free_space | free_percent 
------------+-------------+------------+---------------+------------------+----------------+--------------------+------------+--------------
 2685902848 |    10000000 | 1210000000 |         45.05 |             0 |              0 |                  0 | 1316722516 |        49.02

Die obige Zahl zeigt an, dass alle Aufblähungen (tote Tupel) verschwunden sind.

Schauen wir uns die Auswirkungen von VACUUM FULL ANALYZE an und sehen, was passiert:

postgres=# vacuum full analyze pgbench_accounts ;
VACUUM

postgres=# explain analyze select * from pgbench_accounts;

                            QUERY PLAN                                                            
---------------------------------------------------------------------------
 Seq Scan on pgbench_accounts  (cost=0.00..263935.35 rows=10000035 width=97) 
(actual time=0.015..1089.726 rows=10000000 loops=1)
 Planning time: 0.148 ms
 Execution time: 1532.596 ms

Wie Sie sehen, ähneln die Zahlen für die tatsächliche Zeit und die Ausführungszeit den Zahlen vor UPDATE. Außerdem ist die Größe der Tabelle jetzt von 2 GB auf 1 GB gesunken.

postgres=# select pg_relation_size('pgbench_accounts')/1024/1024/1024;

 ?column? 
-----------------
        1

Das ist die Wirkung von VACUUM FULL.

FÜLLFAKTOR

FILLFACTOR ist ein sehr wichtiges Attribut, das für die Datenbankwartungsstrategie auf Tabellen- und Indexebene einen echten Unterschied machen kann. Dieser Wert gibt den Speicherplatz an, der von den INSERTs innerhalb eines Datenblocks verwendet werden soll. Der FILLFACTOR-Wert ist standardmäßig auf 100 % eingestellt, was bedeutet, dass INSERTs den gesamten verfügbaren Platz in einem Datenblock nutzen können. Es bedeutet auch, dass kein Platz für UPDATEs vorhanden ist. Dieser Wert kann für stark aktualisierte Tabellen auf einen bestimmten Wert verringert werden.

Dieser Parameter kann für jede Tabelle und einen Index konfiguriert werden. Wenn FILLFACTOR auf den optimalen Wert konfiguriert ist, können Sie auch einen echten Unterschied in der VACUUM-Leistung und der Abfrageleistung feststellen. Kurz gesagt, optimale FILLFACTOR-Werte stellen sicher, dass nicht unnötig viele Blöcke zugewiesen werden.

Sehen wir uns dasselbe Beispiel oben an -

Die Tabelle hat eine Million Zeilen

postgres=# select count(*) From pgbench_accounts ;
  count   
-----------------
 10000000

Vor dem Update beträgt die Größe der Tabelle 1 GB

postgres=# select pg_relation_size('pgbench_accounts')/1024/1024/1024;

?column? 
--------
   1

postgres=# update pgbench_accounts set abalance=1;
UPDATE 10000000

Nach dem Update hat sich die Größe der Tabelle nach dem UPDATE auf 2 GB erhöht

postgres=# select pg_relation_size('pgbench_accounts')/1024/1024/1024;

?column? 
---------
    2

Das bedeutet, dass die Anzahl der dem Tisch zugewiesenen Blöcke um 100 % gestiegen ist. Wenn der FILLFACTOR konfiguriert wurde, hat sich die Größe der Tabelle möglicherweise nicht um diesen Rand erhöht.

Woher wissen Sie, welchen Wert Sie für FILLFACTOR konfigurieren müssen?

Es hängt alles davon ab, welche Spalten aktualisiert werden und wie groß die aktualisierten Spalten sind. Im Allgemeinen wäre es gut, den FILLFACTOR-Wert auszuwerten, indem Sie ihn in UAT-Datenbanken testen. Wenn die zu aktualisierenden Spalten beispielsweise 10 % der gesamten Tabelle ausmachen, sollten Sie den Füllfaktor auf 90 % oder 80 % konfigurieren.

Wichtiger Hinweis:
Wenn Sie den FILLFACTOR-Wert für die vorhandene Tabelle mit den Daten ändern, müssen Sie ein VACUUM FULL oder eine Reorganisation der Tabelle durchführen, um sicherzustellen, dass der FILLFACTOR-Wert für die vorhandenen Daten wirksam ist.

TIPPS zum Staubsaugen

  • Wie oben gesagt, sollten Sie in Betracht ziehen, den Job VACUUM ANALYZE jede Nacht manuell auf den stark genutzten Tabellen auszuführen, selbst wenn die automatische Bereinigung aktiviert ist.
  • Erwägen Sie, VACUUM ANALYZE auf Tabellen nach Massen-INSERT auszuführen. Dies ist wichtig, da viele glauben, dass VACUUMing nach INSERTs möglicherweise nicht erforderlich ist.
  • Überwachen Sie, um sicherzustellen, dass hochaktive Tabellen regelmäßig VACUUMiert werden, indem Sie die Tabelle pg_stat_user_tables abfragen.
  • Verwenden Sie das Contrib-Modul pg_stattuple, um die Größe des aufgeblähten Platzes innerhalb der Tabellensegmente zu identifizieren.
  • Das Dienstprogramm VACUUM FULL kann nicht auf Produktionsdatenbanksystemen verwendet werden. Erwägen Sie die Verwendung von Tools wie pg_reorg oder pg_repack, die helfen, Tabellen und Indizes online ohne Sperren neu zu organisieren.
  • Stellen Sie sicher, dass der AUTOVACUUM-Prozess während der Geschäftszeiten (mit hohem Verkehrsaufkommen) länger läuft.
  • Aktivieren Sie den Parameter log_autovacuum_min_duration, um die Zeiten und die Dauer von AUTOVACUUM-Prozessen zu protokollieren.
  • Stellen Sie vor allem sicher, dass FILLFACTOR für Tabellen und Indizes mit vielen Transaktionen auf einen optimalen Wert konfiguriert ist.
Laden Sie noch heute das Whitepaper PostgreSQL-Verwaltung und -Automatisierung mit ClusterControl herunterErfahren Sie, was Sie wissen müssen, um PostgreSQL bereitzustellen, zu überwachen, zu verwalten und zu skalierenLaden Sie das Whitepaper herunter

Andere E/A-Probleme

Festplattensortierung

Sortierende Abfragen sind ein weiteres häufiges Vorkommnis in Echtzeit-Produktionsdatenbanken, und die meisten davon lassen sich nicht vermeiden. Abfragen, die Klauseln wie GROUP BY, ORDER BY, DISTINCT, CREATE INDEX, VACUUM FULL usw. verwenden, führen eine Sortierung durch, und die Sortierung kann auf der Festplatte stattfinden. Die Sortierung findet im Speicher statt, wenn die Auswahl und Sortierung basierend auf indizierten Spalten erfolgt. Hier spielen Composite-Indizes eine Schlüsselrolle. Indizes werden aggressiv im Arbeitsspeicher zwischengespeichert. Andernfalls würde sich die Leistung drastisch verlangsamen, wenn die Daten auf der Festplatte sortiert werden müssen.

Um sicherzustellen, dass die Sortierung im Speicher stattfindet, kann der Parameter work_mem verwendet werden. Dieser Parameter kann auf einen Wert konfiguriert werden, sodass die gesamte Sortierung im Speicher erfolgen kann. Der Kernvorteil dieses Parameters besteht darin, dass er neben der Konfiguration in der postgresql.conf auch auf Sitzungsebene, Benutzerebene oder Datenbankebene konfiguriert werden kann. Wie hoch sollte der work_mem-Wert sein? Woher wissen, welche Abfragen eine Datenträgersortierung durchführen? Wie überwacht man Abfragen, die eine Datenträgersortierung in einer Echtzeit-Produktionsdatenbank durchführen?

Die Antwort lautet:Konfigurieren Sie den Parameter log_temp_files auf einen bestimmten Wert. Der Wert wird in Byte angegeben, ein Wert von 0 protokolliert alle temporären Dateien (zusammen mit ihren Größen), die aufgrund der Datenträgersortierung auf der Festplatte generiert wurden. Sobald der Parameter konfiguriert ist, können Sie die folgenden Meldungen in den Protokolldateien sehen

2018-06-07 22:48:02.358 IST [4219] LOG:  temporary file: path "base/pgsql_tmp/pgsql_tmp4219.0", size 200425472
2018-06-07 22:48:02.358 IST [4219] STATEMENT:  create index bid_idx on pgbench_accounts(bid);
2018-06-07 22:48:02.366 IST [4219] LOG:  duration: 6421.705 ms  statement: create index bid_idx on pgbench_accounts(bid);

Die obige Meldung bedeutet, dass die CREATE INDEX-Abfrage eine Datenträgersortierung durchführte und eine Datei mit einer Größe von 200425472 Bytes erzeugt hat, was 191+ MB entspricht. Das bedeutet genau, dass der work_mem-Parameter auf 191+ MB oder mehr konfiguriert werden muss, damit diese spezielle Abfrage eine Speichersortierung durchführen kann.

Nun, für die Anwendungsabfragen kann der work_mem-Parameter nur auf Benutzerebene konfiguriert werden. Beachten Sie zuvor die Anzahl der Verbindungen, die der Benutzer mit der Datenbank herstellt, und die Anzahl der Sortierabfragen, die von diesem Benutzer ausgeführt werden. Weil PostgreSQL versucht, work_mem jedem Prozess (der eine Sortierung durchführt) in jeder Verbindung zuzuweisen, was möglicherweise den Speicher auf dem Datenbankserver verhungern könnte.

Dateisystem-Layout der Datenbank

Das Entwerfen eines effizienten und leistungsfördernden Layouts des Datenbankdateisystems ist aus Sicht der Leistung und Skalierbarkeit wichtig. Wichtig ist, dass dies nicht von der Datenbankgröße abhängt. Im Allgemeinen wird angenommen, dass große Datenbanken eine Hochleistungs-Festplattenarchitektur benötigen, was NICHT zutrifft. Selbst wenn die Datenbankgröße 50 GB beträgt, benötigen Sie möglicherweise eine gute Festplattenarchitektur. Und dies ist möglicherweise nicht ohne zusätzliche Kosten möglich.

Hier sind einige TIPPS dazu:

  • Stellen Sie sicher, dass die Datenbank mehrere Tablespaces hat, wobei Tabellen und Indizes basierend auf den Transaktionsraten gruppiert sind.
  • Der Tablespace muss für ausgeglichene E/A über mehrere Plattendateisysteme verteilt werden. Dadurch wird auch sichergestellt, dass mehrere CPUs ins Spiel kommen, um Transaktionen über mehrere Festplatten hinweg durchzuführen.
  • Erwägen Sie, das Verzeichnis pg_xlog oder pg_wal auf einer separaten Festplatte in einer Datenbank mit vielen Transaktionen zu platzieren.
  • Stellen Sie sicher, dass *_cost-Parameter basierend auf der Infrastruktur konfiguriert sind
  • Verwenden Sie iostat, mpstat und andere E/A-Überwachungstools, um die E/A-Statistiken auf allen Festplatten zu verstehen und die Datenbankobjekte entsprechend zu gestalten/verwalten.

PostgreSQL in der Cloud

Die Infrastruktur ist entscheidend für eine gute Datenbankleistung. Performance-Engineering-Strategien unterscheiden sich je nach Infrastruktur und Umgebung. Bei PostgreSQL-Datenbanken, die in der Cloud gehostet werden, ist besondere Vorsicht geboten. Leistungsbenchmarks für Datenbanken, die auf physischen Barebone-Servern in einem lokalen Rechenzentrum gehostet werden, können sich völlig von Datenbanken unterscheiden, die in der Public Cloud gehostet werden.

Im Allgemeinen könnten Cloud-Instanzen etwas langsamer sein und Benchmarks unterscheiden sich erheblich, insbesondere in Bezug auf I/O. Führen Sie immer E/A-Latenzprüfungen durch, bevor Sie eine Cloud-Instanz auswählen/erstellen. Zu meiner Überraschung habe ich erfahren, dass die Leistung von Cloud-Instanzen auch je nach Region variieren kann, obwohl sie vom selben Cloud-Anbieter stammen. Um dies weiter zu erläutern:Eine Cloud-Instanz mit denselben Spezifikationen, die in zwei verschiedenen Regionen erstellt wurde, könnte Ihnen unterschiedliche Leistungsergebnisse liefern.

Laden von Massendaten

Offline-Ladevorgänge für Massendaten sind in der Datenbankwelt weit verbreitet. Sie können eine erhebliche E/A-Last erzeugen, was wiederum die Datenladeleistung verlangsamt. Ich habe mich in meiner Erfahrung als DBA solchen Herausforderungen gestellt. Oft wird das Laden von Daten schrecklich langsam und muss angepasst werden. Hier sind einige Tipps. Beachten Sie, dass diese nur für Offline-Datenladevorgänge gelten und nicht für das Laden von Daten in eine Live-Produktionsdatenbank berücksichtigt werden können.

  • Da die meisten Datenladevorgänge außerhalb der Geschäftszeiten ausgeführt werden, stellen Sie sicher, dass die folgenden Parameter während des Datenladens konfiguriert sind -
    • Konfigurieren Sie Checkpoint-bezogene Werte, die groß genug sind, damit Checkpoints keine Leistungsprobleme verursachen.
    • Full_page_write ausschalten
    • Wanderarchivierung ausschalten
    • Konfigurieren Sie den Parameter „synchronous_commit“ auf „off“
    • Löschen Sie Beschränkungen und Indizes für die Tabellen, die der Datenlast unterliegen (Beschränkungen und Indizes können nach der Datenlast mit einem größeren work_mem-Wert neu erstellt werden)
    • Wenn Sie die Daten aus einer CSV-Datei laden, können Sie mit größerem maintenance_work_mem gute Ergebnisse erzielen.
    • Obwohl es einen erheblichen Leistungsvorteil geben wird, schalten Sie den fsync-Parameter NICHT aus, da dies zu Datenbeschädigungen führen könnte.

TIPPS zur Cloud-Leistungsanalyse

  • Führen Sie gründliche E/A-Latenztests mit pgbench durch. Meiner Erfahrung nach hatte ich ziemlich gewöhnliche Leistungsergebnisse, wenn ich im Rahmen der TPS-Evaluierung Festplattenlatenzprüfungen durchführte. Bei einigen öffentlichen Cloud-Instanzen gab es Probleme mit der Cache-Leistung. Dies hilft bei der Auswahl der geeigneten Spezifikationen für die für die Datenbanken ausgewählte Cloud-Instanz.
  • Cloud-Instanzen können von Region zu Region unterschiedlich funktionieren. Eine Cloud-Instanz mit bestimmten Spezifikationen in einer Region kann im Vergleich zu einer Cloud-Instanz mit denselben Spezifikationen in einer anderen Region andere Leistungsergebnisse liefern. Meine pgbench-Tests, die auf mehreren Cloud-Instanzen (alle dieselben Spezifikationen mit demselben Cloud-Anbieter) in verschiedenen Regionen ausgeführt wurden, ergaben bei einigen unterschiedliche Ergebnisse. Dies ist besonders wichtig, wenn Sie in die Cloud migrieren.
  • Die Abfrageleistung in der Cloud erfordert möglicherweise einen anderen Optimierungsansatz. DBAs müssen *_cost-Parameter verwenden, um sicherzustellen, dass fehlerfreie Abfrageausführungspläne generiert werden.

Tools zur Überwachung der PostgreSQL-Leistung

There are various tools to monitor PostgreSQL performance. Let me highlight some of those.

  • pg_top is a GREAT tool to monitor PostgreSQL database dynamically. I would highly recommend this tool for DBAs for various reasons. This tool has numerous advantages, let me list them out:
    • pg_top tool uses textual interface and is similar to Unix “top” utility.
    • Will clearly list out the processes and the hardware resources utilized. What excites me with this tool is that it will clearly tell you if a particular process is currently on DISK or CPU - in my view that’s excellent. DBAs can clearly pick the process running for longer time on the disk.
    • You can check the EXPLAIN PLAN of the top SQLs dynamically or instantly
    • You can also find out what Tables or Indexes are being scanned instantly
  • Nagios is a popular monitoring tool for PostgreSQL which has both open-source and commercial versions. Open source version should suffice for monitoring. Custom Perl scripts can be built and plugged into Nagios module.
  • Pgbadger is a popular tool which can be used to analyze PostgreSQL log files and generate performance reports. This report can be used to analyze the performance of checkpoints, disk sorting.
  • Zabbix is another popular tool used for PostgreSQL monitoring.

ClusterControl is an up-and-coming management platform for PostgreSQL. Apart from monitoring, it also has functionality to deploy replication setups with load balancers, automatic failover, backup management, among others.