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

Ein Performance-Spickzettel für PostgreSQL

Leistung ist eine der wichtigsten und komplexesten Aufgaben bei der Verwaltung einer Datenbank. Dies kann durch die Konfiguration, die Hardware oder sogar das Design des Systems beeinflusst werden. Standardmäßig ist PostgreSQL im Hinblick auf Kompatibilität und Stabilität konfiguriert, da die Leistung stark von der Hardware und unserem System selbst abhängt. Wir können ein System haben, in dem viele Daten gelesen werden, aber die Informationen ändern sich nicht häufig. Oder wir können ein System haben, das kontinuierlich schreibt. Aus diesem Grund ist es unmöglich, eine Standardkonfiguration zu definieren, die für alle Arten von Workloads funktioniert.

In diesem Blog werden wir sehen, wie man die Arbeitslast oder Abfragen analysiert, die ausgeführt werden. Wir werden dann einige grundlegende Konfigurationsparameter überprüfen, um die Leistung unserer PostgreSQL-Datenbank zu verbessern. Wie bereits erwähnt, sehen wir nur einige der Parameter. Die Liste der PostgreSQL-Parameter ist umfangreich, wir möchten nur einige der wichtigsten ansprechen. Man kann jedoch jederzeit die offizielle Dokumentation konsultieren, um sich mit den Parametern und Konfigurationen zu befassen, die in unserer Umgebung am wichtigsten oder nützlichsten erscheinen.

ERKLÄREN

Einer der ersten Schritte, die wir unternehmen können, um zu verstehen, wie wir die Leistung unserer Datenbank verbessern können, ist die Analyse der gestellten Abfragen.

PostgreSQL entwickelt einen Abfrageplan für jede empfangene Abfrage. Um diesen Plan anzuzeigen, verwenden wir EXPLAIN.

Die Struktur eines Abfrageplans ist eine Baumstruktur von Planknoten. Die Knoten in der unteren Ebene des Baums sind Scan-Knoten. Sie geben rohe Zeilen aus einer Tabelle zurück. Es gibt verschiedene Arten von Scan-Knoten für verschiedene Zugriffsmethoden auf die Tabelle. Die EXPLAIN-Ausgabe hat eine Zeile für jeden Knoten in der Planstruktur.

world=# EXPLAIN SELECT * FROM city t1,country t2 WHERE id>100 AND t1.population>700000 AND t2.population<7000000;
                               QUERY PLAN                                
--------------------------------------------------------------------------
Nested Loop  (cost=0.00..734.81 rows=50662 width=144)
  ->  Seq Scan on city t1  (cost=0.00..93.19 rows=347 width=31)
        Filter: ((id > 100) AND (population > 700000))
  ->  Materialize  (cost=0.00..8.72 rows=146 width=113)
        ->  Seq Scan on country t2  (cost=0.00..7.99 rows=146 width=113)
              Filter: (population < 7000000)
(6 rows)

Dieser Befehl zeigt, wie die Tabellen in unserer Abfrage gescannt werden. Mal sehen, was diesen Werten entspricht, die wir in unserem EXPLAIN beobachten können.

  • Der erste Parameter zeigt die Operation, die die Engine in diesem Schritt mit den Daten durchführt.
  • Geschätzte Anlaufkosten. Dies ist die Zeit, die benötigt wird, bevor die Ausgabephase beginnen kann.
  • Geschätzte Gesamtkosten. Dies wird unter der Annahme angegeben, dass der Planknoten vollständig ausgeführt wird. In der Praxis könnte der Elternknoten eines Knotens damit aufhören, alle verfügbaren Zeilen zu lesen.
  • Geschätzte Anzahl der von diesem Planknoten ausgegebenen Zeilen. Auch hier wird angenommen, dass der Knoten vollständig ausgeführt wird.
  • Geschätzte durchschnittliche Zeilenbreite, die von diesem Planknoten ausgegeben wird.

Der kritischste Teil der Anzeige sind die geschätzten Kosten für die Auszugsausführung, dh die Schätzung des Planers, wie lange es dauern wird, den Auszug auszuführen. Wenn wir vergleichen, wie effektiv eine Abfrage mit der anderen ist, werden wir in der Praxis die Kostenwerte von ihnen vergleichen.

Es ist wichtig zu verstehen, dass die Kosten eines übergeordneten Knotens die Kosten aller seiner untergeordneten Knoten beinhalten. Es ist auch wichtig zu erkennen, dass die Kosten nur Dinge widerspiegeln, die dem Planer wichtig sind. Insbesondere berücksichtigen die Kosten nicht die Zeit, die für die Übertragung von Ergebniszeilen an den Client aufgewendet wurde, was ein wichtiger Faktor für die tatsächlich verstrichene Zeit sein könnte; aber der Planer ignoriert es, weil er es nicht ändern kann, indem er den Plan ändert.

Die Kosten werden in willkürlichen Einheiten gemessen, die durch die Kostenparameter des Planers bestimmt werden. Traditionelle Praxis besteht darin, die Kosten in Einheiten von Plattenseitenabrufen zu messen; das heißt, seq_page_cost wird herkömmlicherweise auf 1,0 gesetzt und die anderen Kostenparameter werden relativ dazu gesetzt.

ANALYSE ERKLÄREN

Mit dieser Option führt EXPLAIN die Abfrage aus und zeigt dann die wahren Zeilenzahlen und die wahre Laufzeit an, die sich in jedem Planknoten angesammelt haben, zusammen mit den gleichen Schätzungen, die ein einfaches EXPLAIN zeigt.

Sehen wir uns ein Beispiel für die Verwendung dieses Tools an.

world=# EXPLAIN ANALYZE SELECT * FROM city t1,country t2 WHERE id>100 AND t1.population>700000 AND t2.population<7000000;
                                                     QUERY PLAN                                                      
----------------------------------------------------------------------------------------------------------------------
Nested Loop  (cost=0.00..734.81 rows=50662 width=144) (actual time=0.081..22.066 rows=51100 loops=1)
  ->  Seq Scan on city t1  (cost=0.00..93.19 rows=347 width=31) (actual time=0.069..0.618 rows=350 loops=1)
        Filter: ((id > 100) AND (population > 700000))
        Rows Removed by Filter: 3729
  ->  Materialize  (cost=0.00..8.72 rows=146 width=113) (actual time=0.000..0.011 rows=146 loops=350)
        ->  Seq Scan on country t2  (cost=0.00..7.99 rows=146 width=113) (actual time=0.007..0.058 rows=146 loops=1)
              Filter: (population < 7000000)
              Rows Removed by Filter: 93
Planning time: 0.136 ms
Execution time: 24.627 ms
(10 rows)

Wenn wir den Grund nicht finden, warum unsere Abfragen länger dauern als sie sollten, können wir in diesem Blog nach weiteren Informationen suchen.

VAKUUM

Der VACUUM-Prozess ist für mehrere Wartungsaufgaben innerhalb der Datenbank verantwortlich, von denen eine die Wiederherstellung von Speicher ist, der von toten Tupeln belegt ist. Im normalen Betrieb von PostgreSQL werden Tupel, die durch ein Update gelöscht oder veraltet sind, nicht physisch aus ihrer Tabelle entfernt; sie bleiben vorhanden, bis ein VAKUUM durchgeführt wird. Daher ist es notwendig, das VACUUM regelmäßig durchzuführen, insbesondere in häufig aktualisierten Tabellen.

Wenn das VAKUUM zu viel Zeit oder Ressourcen in Anspruch nimmt, bedeutet dies, dass wir es häufiger tun müssen, damit bei jedem Vorgang weniger zu reinigen ist.

In jedem Fall müssen Sie VACUUM deaktivieren, beispielsweise wenn Sie Daten in großen Mengen laden.

Der VACUUM gewinnt einfach Platz zurück und stellt ihn zur Wiederverwendung zur Verfügung. Diese Form des Befehls kann parallel zum normalen Lesen und Schreiben der Tabelle arbeiten, da keine exklusive Sperre erhalten wird. Der zusätzliche Speicherplatz wird jedoch (in den meisten Fällen) nicht an das Betriebssystem zurückgegeben; es ist nur für die Wiederverwendung innerhalb derselben Tabelle verfügbar.

VACUUM FULL schreibt den gesamten Inhalt der Tabelle in eine neue Festplattendatei ohne zusätzlichen Speicherplatz, wodurch der ungenutzte Speicherplatz an das Betriebssystem zurückgegeben werden kann. Dieses Formular ist viel langsamer und erfordert während der Verarbeitung eine exklusive Sperre für jede Tabelle.

VACUUM ANALYZE führt ein VACUUM und dann eine ANALYZE für jede ausgewählte Tabelle durch. Dies ist eine praktische Möglichkeit, routinemäßige Wartungsskripte zu kombinieren.

ANALYZE sammelt Statistiken über den Inhalt der Tabellen in der Datenbank und speichert die Ergebnisse in pg_statistic. Anschließend verwendet der Abfrageplaner diese Statistiken, um die effizientesten Ausführungspläne für Abfragen zu ermitteln.

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

Konfigurationsparameter

Um diese Parameter zu ändern, müssen wir die Datei $ PGDATA / postgresql.conf bearbeiten. Wir müssen bedenken, dass einige von ihnen einen Neustart unserer Datenbank erfordern.

max_connections

Bestimmt die maximale Anzahl gleichzeitiger Verbindungen zu unserer Datenbank. Es gibt Speicherressourcen, die pro Client konfiguriert werden können, daher kann die maximale Anzahl von Clients die maximal verwendete Speichermenge vorschlagen.

superuser_reserved_connections

Falls das Limit von max_connection erreicht wird, werden diese Verbindungen für den Superuser reserviert.

shared_buffers

Legt die Speichermenge fest, die der Datenbankserver für gemeinsam genutzte Speicherpuffer verwendet. Wenn Sie einen dedizierten Datenbankserver mit 1 GB oder mehr RAM haben, ist ein angemessener Anfangswert für shared_buffers 25 % Ihres Systemspeichers. Größere Konfigurationen für shared_buffers erfordern im Allgemeinen eine entsprechende Erhöhung von max_wal_size, um den Prozess des Schreibens großer Mengen neuer oder geänderter Daten über einen längeren Zeitraum auszudehnen.

temp_buffers

Legt die maximale Anzahl temporärer Puffer fest, die für jede Sitzung verwendet werden. Dies sind lokale Sitzungspuffer, die nur für den Zugriff auf temporäre Tabellen verwendet werden. Eine Sitzung weist die temporären Puffer nach Bedarf bis zu dem von temp_buffers angegebenen Limit zu.

work_mem

Gibt die Speichermenge an, die von den internen Operationen von ORDER BY, DISTINCT, JOIN und Hash-Tabellen verwendet wird, bevor in die temporären Dateien auf der Festplatte geschrieben wird. Bei der Konfiguration dieses Werts müssen wir berücksichtigen, dass mehrere Sitzungen diese Operationen gleichzeitig ausführen und jede Operation so viel Speicher verwenden darf, wie durch diesen Wert angegeben, bevor sie beginnt, Daten in temporäre Dateien zu schreiben.

Diese Option hieß in älteren Versionen von PostgreSQL sort_mem.

maintenance_work_mem

Gibt die maximale Speichermenge an, die von Wartungsvorgängen verwendet wird, z. B. VACUUM, CREATE INDEX und ALTER TABLE ADD FOREIGN KEY. Da nur eine dieser Operationen gleichzeitig von einer Sitzung ausgeführt werden kann und bei einer Installation normalerweise nicht viele davon gleichzeitig ausgeführt werden, kann sie größer als work_mem sein. Größere Konfigurationen können die Leistung für VACUUM- und Datenbankwiederherstellungen verbessern.

Wenn das Autovacuum ausgeführt wird, kann diesem Speicher die Anzahl der Male zugewiesen werden, in denen der Parameter autovacuum_max_workers konfiguriert ist, also müssen wir dies berücksichtigen, oder ansonsten den Parameter autovacuum_work_mem konfigurieren, um dies separat zu verwalten.

fsync

Wenn fsync aktiviert ist, versucht PostgreSQL sicherzustellen, dass die Updates physisch auf die Festplatte geschrieben werden. Dadurch wird sichergestellt, dass der Datenbank-Cluster nach einem Betriebssystem- oder Hardware-Crash wieder in einen konsistenten Zustand versetzt werden kann.

Während das Deaktivieren von fsync im Allgemeinen die Leistung verbessert, kann es im Falle eines Stromausfalls oder eines Systemabsturzes zu Datenverlust kommen. Daher ist es nur ratsam, fsync zu deaktivieren, wenn Sie Ihre gesamte Datenbank problemlos aus externen Daten neu erstellen können.

checkpoint_segments (PostgreSQL <9.5)

Maximale Anzahl von Aufzeichnungsdateisegmenten zwischen automatischen WAL-Kontrollpunkten (jedes Segment ist normalerweise 16 Megabyte groß). Das Erhöhen dieses Parameters kann die zum Beheben von Fehlern benötigte Zeit verlängern. In einem System mit viel Datenverkehr kann es die Leistung beeinträchtigen, wenn es auf einen sehr niedrigen Wert eingestellt ist. Es wird empfohlen, den Wert von checkpoint_segments auf Systemen mit vielen Datenänderungen zu erhöhen.

Außerdem empfiehlt es sich, die WAL-Dateien auf einem anderen Datenträger als PGDATA zu speichern. Dies ist sowohl für den Schreibausgleich als auch für die Sicherheit im Falle eines Hardwarefehlers nützlich.

Ab PostgreSQL 9.5 wurde die Konfigurationsvariable "checkpoint_segments" entfernt und durch "max_wal_size" und "min_wal_size" ersetzt

max_wal_size (PostgreSQL>=9.5)

Maximale Größe, die der WAL zwischen den Kontrollpunkten wachsen darf. Die Größe von WAL kann unter besonderen Umständen max_wal_size überschreiten. Das Erhöhen dieses Parameters kann die zum Beheben von Fehlern benötigte Zeit verlängern.

min_wal_size (PostgreSQL>=9.5)

Wenn die WAL-Datei unter diesem Wert gehalten wird, wird sie für die zukünftige Verwendung an einem Kontrollpunkt recycelt, anstatt gelöscht zu werden. Dies kann verwendet werden, um sicherzustellen, dass genügend WAL-Speicherplatz reserviert ist, um Spitzen bei der Nutzung von WAL zu bewältigen, beispielsweise wenn große Batch-Jobs ausgeführt werden.

wal_sync_method

Methode, die verwendet wird, um WAL-Aktualisierungen auf der Festplatte zu erzwingen. Wenn fsync deaktiviert ist, hat diese Einstellung keine Auswirkung.

wal_buffers

Die Menge des gemeinsam genutzten Speichers, der für WAL-Daten verwendet wird, die noch nicht auf die Festplatte geschrieben wurden. Die Standardeinstellung ist etwa 3 % von shared_buffers, nicht weniger als 64 KB oder mehr als die Größe eines WAL-Segments (normalerweise 16 MB). Das Festlegen dieses Werts auf mindestens einige MB kann die Schreibleistung auf einem Server mit vielen gleichzeitigen Transaktionen verbessern.

effektive_cache_größe

Dieser Wert wird vom Abfrageplaner verwendet, um Pläne zu berücksichtigen, die möglicherweise in den Arbeitsspeicher passen oder nicht. Dies wird in den Kostenschätzungen der Verwendung eines Index berücksichtigt; Ein hoher Wert macht es wahrscheinlicher, dass Index-Scans verwendet werden, und ein niedriger Wert macht es wahrscheinlicher, dass sequenzielle Scans verwendet werden. Ein vernünftiger Wert wäre 50 % des Arbeitsspeichers.

default_statistics_target

PostgreSQL sammelt Statistiken aus jeder der Tabellen in seiner Datenbank, um zu entscheiden, wie Abfragen darauf ausgeführt werden. Standardmäßig sammelt es nicht zu viele Informationen, und wenn Sie keine guten Ausführungspläne erhalten, sollten Sie diesen Wert erhöhen und dann ANALYZE in der Datenbank erneut ausführen (oder auf das AUTOVACUUM warten).

synchronous_commit

Gibt an, ob der Transaktionscommit darauf wartet, dass die WAL-Datensätze auf die Festplatte geschrieben werden, bevor der Befehl eine „Erfolgsanzeige“ an den Client zurückgibt. Die möglichen Werte sind:„on“, „remote_apply“, „remote_write“, „local“ und „off“. Die Standardeinstellung ist „ein“. Wenn es deaktiviert ist, kann es zwischen der Rückkehr des Clients und dem garantierten Schutz der Transaktion vor einer Serversperre zu einer Verzögerung kommen. Im Gegensatz zu fsync birgt das Deaktivieren dieses Parameters kein Risiko einer Datenbankinkonsistenz:Ein Absturz des Betriebssystems oder der Datenbank kann zum Verlust einiger kürzlich angeblich durchgeführter Transaktionen führen, aber der Status der Datenbank ist genau derselbe wie bei diesen Transaktionen wurde sauber storniert. Daher kann das Deaktivieren von physical_commit eine sinnvolle Alternative sein, wenn Leistung wichtiger ist als die exakte Gewissheit über die Dauerhaftigkeit einer Transaktion.

Protokollierung

Es gibt mehrere Arten von Daten zu protokollieren, die nützlich sein können oder nicht. Sehen wir uns einige davon an:

  • log_min_error_statement:Legt die minimale Protokollierungsstufe fest.
  • log_min_duration_statement:Wird verwendet, um langsame Abfragen im System aufzuzeichnen.
  • log_line_prefix:Fügt Informationen am Anfang jeder Protokollzeile ein.
  • log_statement:Sie können zwischen NONE, DDL, MOD, ALL wählen. Die Verwendung von "all" kann zu Leistungsproblemen führen.

Gestaltung

In vielen Fällen kann das Design unserer Datenbank die Leistung beeinträchtigen. Wir müssen bei unserem Design vorsichtig sein, unser Schema normalisieren und redundante Daten vermeiden. In vielen Fällen ist es praktisch, mehrere kleine Tische anstelle eines großen Tisches zu haben. Aber wie wir bereits gesagt haben, hängt alles von unserem System ab und es gibt keine einzige mögliche Lösung.

Wir müssen auch die Indizes verantwortungsvoll verwenden. Wir sollten keine Indizes für jedes Feld oder jede Kombination von Feldern erstellen, da wir, obwohl wir nicht die gesamte Tabelle durchlaufen müssen, Plattenplatz verbrauchen und Overhead für Schreibvorgänge hinzufügen.

Ein weiteres sehr nützliches Tool ist die Verwaltung des Verbindungspools. Wenn wir ein System mit viel Last haben, können wir dies nutzen, um eine Überlastung der Verbindungen in der Datenbank zu vermeiden und sie wiederverwenden zu können.

Hardware

Wie wir zu Beginn dieses Blogs erwähnt haben, ist die Hardware einer der wichtigen Faktoren, die sich direkt auf die Leistung unserer Datenbank auswirken. Sehen wir uns einige Punkte an, die Sie beachten sollten.

  • Speicher:Je mehr RAM wir haben, desto mehr Speicherdaten können wir verarbeiten, und das bedeutet eine bessere Leistung. Die Schreib- und Lesegeschwindigkeit auf der Festplatte ist viel langsamer als im Speicher. Je mehr Informationen wir also im Speicher haben können, desto besser ist die Leistung.
  • CPU:Vielleicht macht es nicht viel Sinn, das zu sagen, aber je mehr CPU wir haben, desto besser. Auf jeden Fall ist es nicht das Wichtigste in Bezug auf die Hardware, aber wenn wir eine gute CPU haben können, wird sich unsere Verarbeitungskapazität verbessern, und das wirkt sich direkt auf unsere Datenbank aus.
  • Festplatte:Wir haben mehrere Arten von Discs, die wir verwenden können, SCSI, SATA, SAS, IDE. Wir haben auch Solid State Disks. Wir müssen Qualität / Preis vergleichen, was wir verwenden sollten, um seine Geschwindigkeit zu vergleichen. Aber der Festplattentyp ist nicht das einzige, was zu berücksichtigen ist, wir müssen auch sehen, wie man sie konfiguriert. Wenn wir eine gute Leistung wünschen, können wir RAID10 verwenden und die WALs auf einer anderen Festplatte außerhalb des RAIDs halten. Es wird nicht empfohlen, RAID5 zu verwenden, da die Leistung dieses RAID-Typs für Datenbanken nicht gut ist.

Schlussfolgerung

Nachdem wir die in diesem Blog erwähnten Punkte berücksichtigt haben, können wir einen Benchmark durchführen, um das Verhalten der Datenbank zu überprüfen.

Es ist auch wichtig, dass unsere Datenbank überwacht wird, um festzustellen, ob wir mit einem Leistungsproblem konfrontiert sind, und um es so schnell wie möglich lösen zu können. Für diese Aufgabe gibt es mehrere Tools wie Nagios, ClusterControl oder Zabbix, unter anderem, die uns nicht nur die Überwachung ermöglichen, sondern mit einigen von ihnen auch proaktive Maßnahmen ergreifen können, bevor das Problem auftritt. Mit ClusterControl können wir zusätzlich zu Überwachung, Verwaltung und mehreren anderen Dienstprogrammen Empfehlungen dazu erhalten, welche Maßnahmen wir ergreifen können, wenn wir Leistungswarnungen erhalten. Dadurch erhalten wir eine Vorstellung davon, wie potenzielle Probleme gelöst werden können.

Dieser Blog ist nicht als erschöpfender Leitfaden zur Verbesserung der Datenbankleistung gedacht. Hoffentlich gibt es ein klareres Bild davon, welche Dinge wichtig werden können und einige der grundlegenden Parameter, die konfiguriert werden können. Zögern Sie nicht, uns mitzuteilen, wenn wir wichtige vergessen haben.