it-swarm.com.de

Das Erhöhen von work_mem und shared_buffers in Postgres 9.2 verlangsamt Abfragen erheblich

Ich habe eine PostgreSQL 9.2-Instanz, die auf einem RHEL 6.3 8-Core-Computer mit 16 GB RAM ausgeführt wird. Der Server ist dieser Datenbank zugeordnet. Angesichts der Tatsache, dass die Standard-postgresql.conf in Bezug auf die Speichereinstellungen recht konservativ ist, hielt ich es für eine gute Idee, Postgres die Verwendung von mehr Speicher zu ermöglichen. Zu meiner Überraschung hat das Befolgen von Ratschlägen zu wiki.postgresql.org/wiki/Tuning_Your_PostgreSQL_Server praktisch jede von mir ausgeführte Abfrage erheblich verlangsamt, aber bei komplexeren Abfragen ist dies offensichtlich deutlicher.

Ich habe auch versucht, pgtune auszuführen, was die folgende Empfehlung mit mehr eingestellten Parametern gab, aber das hat nichts geändert. Es werden Shared_Buffer von 1/4 der Größe RAM Größe) vorgeschlagen, was den Ratschlägen an anderer Stelle (und insbesondere im PG-Wiki) zu entsprechen scheint.

default_statistics_target = 50
maintenance_work_mem = 960MB
constraint_exclusion = on
checkpoint_completion_target = 0.9
effective_cache_size = 11GB
work_mem = 96MB
wal_buffers = 8MB
checkpoint_segments = 16
shared_buffers = 3840MB
max_connections = 80

Ich habe versucht, die gesamte Datenbank neu zu indizieren, nachdem ich die Einstellungen geändert hatte (mit reindex database), Aber das hat auch nicht geholfen. Ich habe mit shared_buffers und work_mem herumgespielt. Durch allmähliches Ändern der sehr konservativen Standardwerte (128 KB/1 MB) wurde die Leistung allmählich verringert.

Ich habe EXPLAIN (ANALYZE,BUFFERS) bei einigen Abfragen ausgeführt und der Schuldige scheint zu sein, dass Hash Join erheblich langsamer ist. Mir ist nicht klar warum.

Um ein konkretes Beispiel zu nennen, habe ich die folgende Abfrage. Es läuft in der Standardkonfiguration in ~ 2100 ms und in der Konfiguration mit erhöhten Puffergrößen in ~ 3300 ms:

select count(*) from contest c
left outer join contestparticipant cp on c.id=cp.contestId
left outer join teammember tm on tm.contestparticipantid=cp.id
left outer join staffmember sm on cp.id=sm.contestparticipantid
left outer join person p on p.id=cp.personid
left outer join personinfo pi on pi.id=cp.personinfoid
where pi.lastname like '%b%' or pi.firstname like '%a%';

EXPLAIN (ANALYZE,BUFFERS) für die obige Abfrage:

Die Frage ist , warum ich beim Erhöhen der Puffergröße eine verminderte Leistung beobachte. Der Maschine geht definitiv nicht der Speicher aus. Die Zuordnung, wenn der gemeinsam genutzte Speicher im Betriebssystem (shmmax und shmall) auf sehr große Werte festgelegt ist, sollte kein Problem darstellen. Ich erhalte auch keine Fehler im Postgres-Protokoll. Ich verwende Autovacuum in der Standardkonfiguration, aber ich erwarte nicht, dass dies etwas damit zu tun hat. Alle Abfragen wurden im Abstand von wenigen Sekunden auf demselben Computer ausgeführt, nur mit geänderter Konfiguration (und neu gestartetem PG).

Bearbeiten: Ich habe gerade eine besonders interessante Tatsache gefunden: Wenn ich den gleichen Test auf meinem iMac Mitte 2010 (OSX 10.7.5) auch mit Postgres 9.2.1 und 16 GB RAM durchführe, spüre ich keine Verlangsamung. Speziell:

set work_mem='1MB';
select ...; // running time is ~1800 ms
set work_mem='96MB';
select ...' // running time is ~1500 ms

Wenn ich genau dieselbe Abfrage (die obige) mit genau denselben Daten auf dem Server durchführe, erhalte ich 2100 ms mit work_mem = 1 MB und 3200 ms mit 96 MB.

Der Mac verfügt über eine SSD, ist also verständlicherweise schneller, weist jedoch ein Verhalten auf, das ich erwarten würde.

Siehe auch Nachbesprechung zur pgsql-Leistung .

44
Petr Praus

Denken Sie zunächst daran, dass work_mem pro Operation ist und daher ziemlich schnell übermäßig werden kann. Im Allgemeinen würde ich work_mem in Ruhe lassen, bis Sie es brauchen, wenn Sie keine Probleme mit langsamen Sortierungen haben.

Wenn ich mir Ihre Abfragepläne anschaue, fällt mir auf, dass die Puffertreffer bei den beiden Plänen sehr unterschiedlich sind und dass sogar die sequentiellen Scans langsamer sind. Ich vermute, dass das Problem mit Read-Ahead-Caching und weniger Platz dafür zu tun hat. Dies bedeutet, dass Sie den Speicher für die Wiederverwendung von Indizes und gegen das Lesen von Tabellen auf der Festplatte vorspannen.


Ich verstehe, dass PostgreSQL vor dem Lesen von der Festplatte im Cache nach einer Seite sucht, da es nicht wirklich weiß, ob der Betriebssystem-Cache diese Seite enthält. Da die Seiten dann im Cache verbleiben und dieser Cache langsamer als der Betriebssystem-Cache ist, werden die Arten von Abfragen, die schnell sind, gegenüber den Arten, die langsam sind, geändert. Abgesehen von den Problemen mit work_mem sieht es so aus, als ob alle Ihre Abfrageinformationen aus dem Cache stammen, aber es ist eine Frage, welcher Cache.

work_mem: Wie viel Speicher können wir für eine Sortierung oder eine zugehörige Verknüpfungsoperation zuweisen? Dies erfolgt pro Operation, nicht pro Anweisung oder pro Back-End, sodass eine einzelne komplexe Abfrage ein Vielfaches dieser Speichermenge verwenden kann. Es ist nicht klar, ob Sie diese Grenze erreichen, aber es lohnt sich, darauf hinzuweisen und sich dessen bewusst zu sein. Wenn Sie diesen Wert zu weit erhöhen, verlieren Sie Speicher, der möglicherweise für den Lese-Cache und die gemeinsam genutzten Puffer verfügbar ist.

shared_buffers: Wie viel Speicher muss der tatsächlichen PostgreSQL-Seitenwarteschlange zugewiesen werden? Im Idealfall bleibt der interessante Satz Ihrer Datenbank im hier zwischengespeicherten Speicher und in den Lesepuffern. Dadurch wird jedoch sichergestellt, dass die am häufigsten verwendeten Informationen in allen Backends zwischengespeichert und nicht auf die Festplatte übertragen werden. Unter Linux ist dieser Cache erheblich langsamer als der Festplatten-Cache des Betriebssystems, bietet jedoch Garantien dafür, dass der Festplatten-Cache des Betriebssystems nicht funktioniert und für PostgreSQL transparent ist. Dies ist ziemlich klar, wo Ihr Problem liegt.

Wenn wir also eine Anfrage haben, überprüfen wir zuerst die gemeinsam genutzten Puffer, da PostgreSQL dieses Cache genau kennt, und suchen nach den Seiten. Wenn sie nicht vorhanden sind, bitten wir das Betriebssystem, sie aus der Datei zu öffnen. Wenn das Betriebssystem das Ergebnis zwischengespeichert hat, gibt es die zwischengespeicherte Kopie zurück (dies ist schneller als die gemeinsam genutzten Puffer, aber Pg kann nicht erkennen, ob es zwischengespeichert oder aktiviert ist Festplatte, und Festplatte ist viel langsamer, sodass PostgreSQL diese Chance normalerweise nicht nutzt. Beachten Sie, dass dies auch den zufälligen oder den sequentiellen Seitenzugriff betrifft. Mit niedrigeren Einstellungen für shared_buffers erzielen Sie möglicherweise eine bessere Leistung.

Mein Bauchgefühl ist, dass Sie wahrscheinlich eine bessere oder zumindest konsistentere Leistung in Umgebungen mit hoher Parallelität mit größeren Einstellungen für shared_buffer erzielen. Beachten Sie auch, dass PostgreSQL diesen Speicher erfasst und speichert. Wenn also andere Dinge auf dem System ausgeführt werden, enthalten die Lesepuffer Dateien, die von anderen Prozessen gelesen wurden. Es ist ein sehr großes und komplexes Thema. Größere Einstellungen für gemeinsam genutzte Puffer bieten bessere Garantien für die Leistung, liefern jedoch in einigen Fällen möglicherweise weniger Leistung.

31
Chris Travers

Abgesehen von dem scheinbar paradoxen Effekt, dass work_mem verringert die Leistung ( @ Chris hat möglicherweise eine Erklärung), Sie können Ihre Funktion auf mindestens zwei Arten verbessern.

  • Schreibe zwei gefälschte LEFT JOIN 's mit JOIN. Dies könnte den Abfrageplaner verwirren und zu minderwertigen Plänen führen.
SELECT count(*) AS ct
FROM   contest            c
JOIN   contestparticipant cp ON cp.contestId = c.id
JOIN   personinfo         pi ON pi.id = cp.personinfoid
LEFT   JOIN teammember    tm ON tm.contestparticipantid = cp.id
LEFT   JOIN staffmember   sm ON sm.contestparticipantid = cp.id
LEFT   JOIN person        p  ON p.id = cp.personid
WHERE (pi.firstname LIKE '%a%'
OR     pi.lastname  LIKE '%b%')
  • Angenommen, Ihre tatsächlichen Suchmuster sind selektiver, verwenden Sie Trigrammindizes für pi.firstname und pi.lastname zur Unterstützung nicht verankerter LIKE Suchvorgänge. (Kürzere Muster wie '%a%' werden ebenfalls unterstützt, aber ein Index hilft wahrscheinlich nicht bei nicht selektiven Prädikaten.):
CREATE INDEX personinfo_firstname_gin_idx ON personinfo USING gin (firstname gin_trgm_ops);
CREATE INDEX personinfo_lastname_gin_idx  ON personinfo USING gin (lastname gin_trgm_ops);

Oder ein mehrspaltiger Index:

CREATE INDEX personinfo_name_gin_idx ON personinfo USING gin (firstname gin_trgm_ops, lastname gin_trgm_ops);

Sollte Ihre Anfrage etwas schneller machen. Dazu müssen Sie das Zusatzmodul pg_trgm installieren. Details unter diesen verwandten Fragen:


Haben Sie auch versucht, work_memlokal - nur für die aktuelle Transaktion ?

SET LOCAL work_mem = '96MB';

Dies verhindert, dass gleichzeitige Transaktionen auch mehr RAM verbrauchen und sich möglicherweise gegenseitig hungern.

12