Optimieren Sie PostgreSQL für schnelle Tests

203

Ich wechsle von SQLite zu PostgreSQL für eine typische Rails-Anwendung.

Das Problem ist, dass das Ausführen von Spezifikationen mit PG langsam wurde.
Auf SQLite dauerte es ~ 34 Sekunden, auf PG ~ 76 Sekunden, was mehr als 2x langsamer ist .

Daher möchte ich jetzt einige Techniken anwenden, um die Leistung der Spezifikationen ohne Codeänderungen auf SQLite zu bringen (idealerweise nur durch Festlegen der Verbindungsoptionen, was wahrscheinlich nicht möglich ist).

Einige offensichtliche Dinge aus meinem Kopf sind:

  • RAM Disk (gutes Setup mit RSpec unter OSX wäre gut zu sehen)
  • Nicht protokollierte Tabellen (kann sie auf die gesamte Datenbank angewendet werden, damit ich nicht alle Skripte geändert habe?)

Wie Sie vielleicht verstanden haben, ist mir Zuverlässigkeit und der Rest egal (die DB ist hier nur ein Wegwerfartikel).
Ich muss das Beste aus dem PG herausholen und es so schnell wie möglich machen .

Die beste Antwort würde idealerweise die Tricks beschreiben, um genau das zu tun, das Setup und die Nachteile dieser Tricks.

UPDATE: fsync = off + Die Zeit wurde full_page_writes = offnur auf ~ 65 Sekunden (~ -16 Sekunden) verringert. Guter Start, aber weit vom Ziel von 34 entfernt.

UPDATE 2: Ich habe versucht, eine RAM-Disk zu verwenden, aber der Leistungsgewinn lag innerhalb einer Fehlergrenze. Scheint es also nicht wert zu sein.

UPDATE 3: * Ich habe den größten Engpass festgestellt und jetzt laufen meine Spezifikationen genauso schnell wie die von SQLite.

Das Problem war die Datenbankbereinigung, die das Abschneiden durchgeführt hat . Anscheinend ist SQLite dort viel zu schnell.

Um es zu "reparieren", öffne ich vor jedem Test eine Transaktion und rolle sie am Ende zurück.

Einige Zahlen für ~ 700 Tests.

  • Kürzung: SQLite - 34s, PG - 76s.
  • Transaktion: SQLite - 17s, PG - 18s.

2x Geschwindigkeitssteigerung für SQLite. 4x Geschwindigkeitssteigerung für PG.

Dmytrii Nagirniak
quelle
2
Ich bezweifle wirklich, dass es so schnell wie SQLite geht. SQLite mit einem einzigen Benutzer ist wahnsinnig schnell. Das Design von SQLite ist sehr schnell mit geringen Benutzerzahlen und lässt sich schlecht skalieren. Das Design von Pg lässt sich gut skalieren, ist aber für einfache Massenarbeiten mit nur einem Benutzer nicht so schnell.
Craig Ringer
1
Mir ist das klar, aber es gibt einen bestimmten Fall, für den ich hoffe, PG für (Testläufe) zu optimieren, damit es so schnell wie möglich ist. Es macht mir nichts aus, dort etwas langsamer zu sein, aber 2.2x ist etwas zu langsam. Verstehst du, was ich meine?
Dmytrii Nagirniak
+1 Ich würde mich sehr für Updates des RAM-Disk-Ansatzes interessieren, wenn Sie diesbezüglich irgendwelche Ergebnisse haben.
Tscho
@tscho Ich werde es definitiv hier posten. Aber brauche etwas Zeit, da ich an anderen Sachen arbeite und die PG-Sachen im "Hintergrund" "recherchiere".
Dmytrii Nagirniak
wird das Einfügen der Daten Ihres Problems oder die Abfrage ? Es ist nicht klar aus Ihrer Frage.
a_horse_with_no_name

Antworten:

281

Verwenden Sie zunächst immer die neueste Version von PostgreSQL. Leistungsverbesserungen kommen immer wieder, sodass Sie wahrscheinlich Ihre Zeit verschwenden, wenn Sie eine alte Version optimieren. Zum Beispiel verbessert PostgreSQL 9.2 die Geschwindigkeit vonTRUNCATE und fügt natürlich nur Index-Scans hinzu. Auch kleinere Veröffentlichungen sollten immer befolgt werden; Siehe die Versionsrichtlinie .

Nicht

Sie NICHT setzen einen Tabellen auf einer RAM - Disk oder einem anderen nicht-dauerhafte Lagerung .

Wenn Sie einen Tablespace verlieren, kann die gesamte Datenbank beschädigt und ohne großen Aufwand schwer zu verwenden sein. Dies hat nur einen geringen Vorteil gegenüber der Verwendung von UNLOGGEDTabellen und ohnehin viel RAM für den Cache.

Wenn Sie wirklich ein Ramdisk-basiertes System wollen, initdbeinen ganz neuen Cluster auf der Ramdisk, indem initdbSie eine neue PostgreSQL-Instanz auf der Ramdisk erstellen, sodass Sie über eine vollständig verfügbare PostgreSQL-Instanz verfügen.

PostgreSQL-Serverkonfiguration

Beim Testen können Sie Ihren Server für einen nicht dauerhaften, aber schnelleren Betrieb konfigurieren .

Dies ist eine der wenigen akzeptablen Verwendungen für die fsync=offEinstellung in PostgreSQL. Diese Einstellung weist PostgreSQL so ziemlich an, sich nicht mit geordneten Schreibvorgängen oder anderen unangenehmen Dingen zum Schutz der Datenintegrität und zum Schutz vor Unfällen zu befassen, und gibt PostgreSQL die Erlaubnis, Ihre Daten bei einem Stromausfall oder einem Absturz des Betriebssystems vollständig in den Papierkorb zu werfen.

Es ist unnötig zu erwähnen, fsync=offdass Sie Pg niemals in der Produktion aktivieren sollten, es sei denn, Sie verwenden Pg als temporäre Datenbank für Daten, die Sie von einer anderen Stelle aus neu generieren können. Wenn und nur wenn Sie fsync ausschalten, können Sie es auch full_page_writesausschalten, da es dann nichts mehr nützt. Beachten Sie dies fsync=offund full_page_writeswenden Sie es auf Clusterebene an, damit alle Datenbanken in Ihrer PostgreSQL-Instanz betroffen sind .

Für den Produktionsbetrieb können Sie möglicherweise einen verwenden synchronous_commit=offund festlegen commit_delay, da Sie viele der gleichen Vorteile erhalten wie fsync=offohne das riesige Risiko der Datenkorruption. Sie haben ein kleines Fenster für den Verlust aktueller Daten, wenn Sie das asynchrone Festschreiben aktivieren - aber das war's.

Wenn Sie die Möglichkeit haben, die DDL geringfügig zu ändern, können Sie auch UNLOGGEDTabellen in Seite 9.1+ verwenden, um die WAL-Protokollierung vollständig zu vermeiden und einen echten Geschwindigkeitsschub zu erzielen, wenn die Tabellen gelöscht werden, wenn der Server abstürzt. Es gibt keine Konfigurationsoption, mit der alle Tabellen nicht protokolliert werden können. Sie muss während festgelegt werden CREATE TABLE. Dies ist nicht nur gut zum Testen geeignet, sondern auch praktisch, wenn Sie Tabellen mit generierten oder unwichtigen Daten in einer Datenbank haben, die ansonsten Dinge enthält, die Sie für die Sicherheit benötigen.

Überprüfen Sie Ihre Protokolle und prüfen Sie, ob Sie Warnungen vor zu vielen Kontrollpunkten erhalten. Wenn ja, sollten Sie Ihre checkpoint_segments erhöhen . Möglicherweise möchten Sie auch Ihr checkpoint_completion_target optimieren, um das Ausschreiben zu vereinfachen.

Tune shared_buffersIhre Arbeit zu passen. Dies ist vom Betriebssystem abhängig, hängt davon ab, was sonst noch auf Ihrem Computer vor sich geht, und erfordert einige Versuche und Irrtümer. Die Standardeinstellungen sind äußerst konservativ. Möglicherweise müssen Sie das maximale Limit für gemeinsam genutzten Speicher des Betriebssystems erhöhen, wenn Sie shared_buffersunter PostgreSQL 9.2 und darunter erhöhen . 9.3 und höher haben geändert, wie sie Shared Memory verwenden, um dies zu vermeiden.

Wenn Sie nur ein paar Verbindungen verwenden, die viel Arbeit erledigen, erhöhen Sie diese work_mem, um ihnen mehr RAM zum Spielen für Sortierungen usw. zu geben. Beachten Sie, dass eine zu hohe work_memEinstellung Probleme mit zu wenig Speicher verursachen kann, da dies nicht pro Sortierung der Fall ist pro Verbindung, sodass eine Abfrage viele verschachtelte Sortierungen haben kann. Sie müssen nur dann wirklich erhöhen, work_memwenn Sie sehen, dass Sortierungen auf die Festplatte gelangen EXPLAINoder mit der log_temp_filesEinstellung (empfohlen) protokolliert werden. Ein höherer Wert kann jedoch auch dazu führen, dass Pg intelligentere Pläne auswählt.

Wie in einem anderen Poster hier erwähnt, ist es ratsam, das xlog und die Haupttabellen / -indizes nach Möglichkeit auf separaten Festplatten zu speichern. Separate Partitionen sind ziemlich sinnlos, Sie möchten wirklich separate Laufwerke. Diese Trennung hat viel weniger Vorteile, wenn Sie fsync=offmit UNLOGGEDTabellen arbeiten, und fast keine, wenn Sie Tabellen verwenden.

Schließlich optimieren Sie Ihre Abfragen. Stellen Sie sicher , dass Ihre random_page_costund seq_page_costdie Leistung Ihres Systems widerspiegeln, sicherzustellen , dass Ihr effective_cache_sizerichtig, usw. Verwenden EXPLAIN (BUFFERS, ANALYZE)einzelnen Abfragepläne zu prüfen, und drehen Sie das auto_explainModul auf alle langsame Abfragen zu melden. Sie können die Abfrageleistung häufig erheblich verbessern, indem Sie einen geeigneten Index erstellen oder die Kostenparameter anpassen.

AFAIK Es gibt keine Möglichkeit, eine gesamte Datenbank oder einen Cluster als festzulegen UNLOGGED. Es wäre interessant, dies tun zu können. Fragen Sie auf der PostgreSQL-Mailingliste nach.

Optimierung des Host-Betriebssystems

Es gibt einige Optimierungen, die Sie auch auf Betriebssystemebene vornehmen können. Die Hauptsache, die Sie möglicherweise tun möchten, ist, das Betriebssystem davon zu überzeugen, Schreibvorgänge nicht aggressiv auf die Festplatte zu übertragen, da es Ihnen wirklich egal ist, wann / ob sie es auf die Festplatte schaffen.

Unter Linux können Sie diese Steuerung mit dem virtuellen Speicher - Subsystem ‚s - dirty_*Einstellungen, wie dirty_writeback_centisecs.

Das einzige Problem bei der Optimierung der Rückschreibeinstellungen ist, dass ein Flush durch ein anderes Programm dazu führen kann, dass alle angesammelten PostgreSQL-Puffer ebenfalls geleert werden, was zu großen Verzögerungen führt, während beim Schreiben alles blockiert wird. Möglicherweise können Sie dies beheben, indem Sie PostgreSQL auf einem anderen Dateisystem ausführen. Einige Flushes können jedoch auf Geräte- oder Host-Ebene und nicht auf Dateisystemebene ausgeführt werden, sodass Sie sich nicht darauf verlassen können.

Diese Abstimmung erfordert wirklich das Herumspielen mit den Einstellungen, um zu sehen, was für Ihre Arbeitslast am besten funktioniert.

Auf neueren Kerneln möchten Sie möglicherweise sicherstellen, dass dieser vm.zone_reclaim_modeWert auf Null gesetzt ist, da dies aufgrund von Interaktionen mit der Verwaltung von PostgreSQL zu schwerwiegenden Leistungsproblemen bei NUMA-Systemen führen kann (die meisten Systeme heutzutage) shared_buffers.

Abfrage- und Workload-Optimierung

Dies sind Dinge, die Codeänderungen erfordern; Sie passen möglicherweise nicht zu Ihnen. Einige Dinge können Sie möglicherweise anwenden.

Wenn Sie keine Arbeit in größere Transaktionen stapeln, starten Sie. Viele kleine Transaktionen sind teuer, daher sollten Sie Dinge stapeln, wann immer dies möglich und praktisch ist. Wenn Sie asynchrones Commit verwenden, ist dies weniger wichtig, wird aber dennoch dringend empfohlen.

Verwenden Sie nach Möglichkeit temporäre Tabellen. Sie generieren keinen WAL-Verkehr und sind daher für Einfügungen und Aktualisierungen viel schneller. Manchmal lohnt es sich, eine Reihe von Daten in eine temporäre Tabelle zu schlürfen, sie nach Bedarf zu bearbeiten und sie dann INSERT INTO ... SELECT ...in die endgültige Tabelle zu kopieren. Beachten Sie, dass temporäre Tabellen pro Sitzung sind. Wenn Ihre Sitzung endet oder Sie Ihre Verbindung verlieren, wird die temporäre Tabelle gelöscht und keine andere Verbindung kann den Inhalt der temporären Tabelle (n) einer Sitzung sehen.

Wenn Sie PostgreSQL 9.1 oder höher verwenden, können Sie UNLOGGEDTabellen für Daten verwenden, deren Verlust Sie sich leisten können, z. B. den Sitzungsstatus. Diese sind in verschiedenen Sitzungen sichtbar und werden zwischen Verbindungen beibehalten. Sie werden abgeschnitten, wenn der Server unsauber heruntergefahren wird, sodass sie nicht für alles verwendet werden können, was Sie nicht neu erstellen können. Sie eignen sich jedoch hervorragend für Caches, materialisierte Ansichten, Statustabellen usw.

Im Allgemeinen nicht DELETE FROM blah;. Verwenden Sie TRUNCATE TABLE blah;stattdessen; Es ist viel schneller, wenn Sie alle Zeilen in einer Tabelle ausgeben. Schneiden Sie viele Tabellen in einem TRUNCATEAufruf ab, wenn Sie können. Es gibt jedoch eine Einschränkung, wenn Sie immer wieder viele TRUNCATESkleine Tische spielen. siehe: Postgresql-Kürzungsgeschwindigkeit

Wenn Sie keine Indizes für Fremdschlüssel haben, sind DELETEdie Primärschlüssel, auf die diese Fremdschlüssel verweisen, fürchterlich langsam. Stellen Sie sicher, dass Sie solche Indizes erstellen, wenn Sie dies jemals DELETEvon den referenzierten Tabellen erwarten . Indizes sind für nicht erforderlich TRUNCATE.

Erstellen Sie keine Indizes, die Sie nicht benötigen. Jeder Index hat Wartungskosten. Versuchen Sie, einen minimalen Satz von Indizes zu verwenden, und lassen Sie Bitmap-Index-Scans diese kombinieren, anstatt zu viele große, teure mehrspaltige Indizes zu verwalten. Wenn Indizes erforderlich sind, versuchen Sie zuerst, die Tabelle zu füllen, und erstellen Sie am Ende Indizes.

Hardware

Wenn Sie genug RAM haben, um die gesamte Datenbank zu speichern, ist dies ein großer Gewinn, wenn Sie es verwalten können.

Wenn Sie nicht über genügend RAM verfügen, ist der Speicher umso besser, je schneller er ist. Sogar eine billige SSD macht einen großen Unterschied gegenüber rotierendem Rost. Vertrauen Sie billigen SSDs für die Produktion nicht, sie sind oft nicht absturzsicher und fressen möglicherweise Ihre Daten.

Lernen

Greg Smiths Buch PostgreSQL 9.0 High Performance bleibt relevant, obwohl auf eine etwas ältere Version verwiesen wird. Es sollte eine nützliche Referenz sein.

Treten Sie der allgemeinen PostgreSQL-Mailingliste bei und folgen Sie ihr.

Lesen:

Craig Ringer
quelle
10
Ich kann auch PostgreSQL 9.0 High Performance von @GregSmith empfehlen, es ist wirklich eine großartige Lektüre. Das Buch behandelt alle Aspekte der Leistungsoptimierung vom Festplattenlayout bis zur Abfrageoptimierung und vermittelt Ihnen ein sehr gutes Verständnis der PG-Interna.
Tscho
10
Ich habe kein Update für das Buch für PostgreSQL 9.1 veröffentlicht, das einzige Release seit seiner Veröffentlichung, da es in 9.1 nicht genügend leistungsbezogene Änderungen gab, um dies zu rechtfertigen.
Greg Smith
3
Großartige Zusammenfassung. Nur als winziges Update gilt unter PostgreSQL 9.3 ( postgresql.org/docs/9.3/static/release-9-
Gunnlaugur Briem
1
@brauliobo Meine Tests machen oft viele Sendungen mit hohem TPS ... weil ich versuche, die Produktion zu simulieren, einschließlich paralleler Workloads. Wenn Sie "Einzelverbindung, lineares Testen" meinen, würde ich Ihnen zustimmen.
Craig Ringer
1
stackoverflow.com/questions/11419536/… DELETE ist möglicherweise schneller als TRUNCATE für Tabellen mit wenigen Zeilen, was bei Tests wahrscheinlich der Fall ist.
Jonathan Crosmer
9

Verwenden Sie ein anderes Festplattenlayout:

  • andere Festplatte für $ PGDATA
  • andere Festplatte für $ PGDATA / pg_xlog
  • andere Festplatte für temporäre Dateien (pro Datenbank $ PGDATA / base // pgsql_tmp) (siehe Hinweis zu work_mem)

postgresql.conf optimiert:

  • shared_memory: 30% des verfügbaren Arbeitsspeichers, jedoch nicht mehr als 6 bis 8 GB. Es scheint besser zu sein, weniger gemeinsam genutzten Speicher (2 GB - 4 GB) für schreibintensive Workloads zu haben
  • work_mem: Meistens für ausgewählte Abfragen mit Sortierungen / Aggregationen. Dies erfolgt pro Verbindungseinstellung, und die Abfrage kann diesen Wert mehrmals zuweisen. Wenn die Daten nicht passen, wird die Festplatte verwendet (pgsql_tmp). Aktivieren Sie "Analyse erklären", um festzustellen, wie viel Speicher Sie benötigen
  • fsync und synchronous_commit: Standardwerte sind sicher, aber wenn Sie Datenverlust tolerieren können, können Sie diese deaktivieren
  • random_page_cost: Wenn Sie über eine SSD oder ein schnelles RAID-Array verfügen, können Sie diese für SSD auf 2,0 (RAID) oder sogar auf (1,1) senken
  • checkpoint_segments: Sie können 32 oder 64 höher gehen und checkpoint_completion_target auf 0.9 ändern. Ein niedrigerer Wert ermöglicht eine schnellere Wiederherstellung nach einem Absturz
mys
quelle
4
Beachten Sie fsync=off, dass sich das Speichern von pg_xlog auf einer separaten Festplatte nicht mehr wesentlich verbessert , wenn Sie bereits mit arbeiten.
intgr
Der Wert von 1,1 für SSD scheint sehr unqualifiziert zu sein. Ich gebe zu, dass es das ist, was einige Fachleute blind empfohlen haben. Selbst SSDs sind für sequentielle Lesevorgänge erheblich schneller als zufällige Lesevorgänge.
Acumenus
@ABB Ja, aber Sie haben auch OS-Puffer-Caching-Effekte bei der Arbeit. Alle diese Parameter sind sowieso ein bisschen handgewellt ...
Craig Ringer