undurchsichtige Index-(Nicht-)Nutzung - extrem langsam

Liebe Experten,

ich verstehe nicht so ganz, warum sich der Query-Planer bei zwei sehr änlichen Anfragen sehr unterschiedlich entscheidet.
a) select count(id) from arbeitsplan where status = 2 and jobid = 2665390
b) select count(id) from arbeitsplan where status = 1 and jobid = 2665390

explain analyze für a)

QUERY PLAN
Aggregate  (cost=87423.94..87423.95 rows=1 width=4) (actual time=16.580..16.580 rows=1 loops=1)
  ->  Index Scan using jobidindex on arbeitsplan  (cost=0.00..87391.61 rows=12933 width=4) (actual time=0.248..15.491 rows=3344 loops=1)
        Index Cond: (jobid = 2665390)
        Filter: (status = 2)
Total runtime: 16.770 ms

und für b)

QUERY PLAN
Aggregate  (cost=20487.58..20487.59 rows=1 width=4) (actual  time=2964.944..2964.945 rows=1 loops=1)
  ->  Bitmap Heap Scan on arbeitsplan  (cost=20139.64..20487.36  rows=87 width=4) (actual time=2963.621..2964.797 rows=656 loops=1)
        Recheck Cond: ((jobid = 2665390) AND (status = 1))
        ->  BitmapAnd  (cost=20139.64..20139.64 rows=87 width=0)  (actual time=2960.184..2960.184 rows=0 loops=1)
              ->  Bitmap Index Scan on jobidindex   (cost=0.00..1351.52 rows=43273 width=0) (actual time=2.762..2.762  rows=7344 loops=1)
                    Index Cond: (jobid = 2665390)
              ->  Bitmap Index Scan on statusindex   (cost=0.00..18787.82 rows=641874 width=0) (actual  time=2956.954..2956.954 rows=1086709 loops=1)
                    Index Cond: (status = 1)
Total runtime: 2965.170 ms

Die Tabelle ist relativ lang (ca. 220 Mio Einträge), davon haben ca. 130 Mio im Moment status = 2 und zwischen 1000 und 1 Mio status = 1 (1 heißt gerade in Bearbeitung).
Mit o.g. jobid sind die Werte 2500 für status = 2 und 1400 für status = 1 gewesen.

Es gibt einen Index ohne Bedingungen mit btree auf Spalte jobid und einen auf Spalte status. Ich verstehe einfach nicht, warum diese nicht genutzt werden. Oder müssen die anders aussehen?

PostgreSQL Version ist 8.4.3

Kennt jemand das Problem und weiß, wie ich ihm beikommen kann? Die Suche mit status = 1 kommt in meiner Anwendung doch öfters vor und bremst alles massiv aus. Im Moment läuft deswegen nix, denn außerdem ist noch ein autovaccuum auf der Tabelle seit ein paar Tagen aktiv und ich habe wegen des Problems “analyze table” angeworfen, aber der läuft jetzt auch schon zwei Tage… :frowning:

Ich würde mich über jeden Hinweis freuen!
Beste Grüße
Jonathan

Ist jobid unique, und falls ja, ist da ein unique index drauf?

Hast mal einen Index auf (jobid,status) probiert?


Okay, sehe grade, jobid scheint nicht unique zu sein…

Aber daß 130 Millionen im Status = 2 sind scheint mir nicht wahr zu sein.

Andreas

status = 2 haben inzwischen sogar 148110039 Zeilen. (davon aber nur ein paar Tausend mit jobid = 2665390, wie gesagt)

Einen Index auf (jobid, status) habe ich noch nicht probiert. Der könnte dann allerdings auch nicht unique sein, da noch eine weitere Spalte (reihenfolge) mit reinkommt. Und selbst dann sind - obwohl die dann theoretisch unique sein müsste - bei meinen Daten zumindest für status = 2 die Daten immer noch nicht unique.

Würde ein solcher Index dann sicher verwendet werden?

Denn es ist ja auch im obigen Fall so, dass die Sache sehr viel schneller gehen würde, wenn er nur die Indizes verwenden würde. Und dass es theoretisch geht, zeigt ja die Abfrage mit status = 2, wo alles passt. Da muss m.E. also doch irgendwas schief laufen, aber ich verstehe nicht, was es ist :frowning:

Aber der Execution Plan der ersten Abfrage sagt was anderes: “(rows=1 loops=1)”

Soweit ich das sehe, ist die zweite Abfrage durch die Verwendung des statusindex so langsam.
Wieviele verschiedene Werte gibt es denn für status? (Beim Wort “Status” würde ich mal denken, vielleicht 10 oder so)

Wenn die in etwa gleichverteilt sind, dann bringt ein Scan über den Index nich wirklich viel weil jeder Wert dann ca. 12 Mio mal vorkommt.

Ich denke auch, dass ein kombinierter Index (jobid, status) mehr bringt (muss ja nicht unique sein).
Oder versuchsweise den Index über status mal ganz weglassen.

wegen des Problems “analyze table” angeworfen, aber der läuft jetzt auch schon zwei Tage…

Da stimmt doch irgendwas nicht. So lange darf ein analyze nicht dauern.

Das geht ja rasend schnell bei Euch!
rows=1 erkläre ich mir durch den count(id), der ja tatsächlich nur eine Zeile zurückgibt. Oder verstehe ich das nicht richtig?

Beim statusindex hast Du recht: Es gibt in dieser Tabelle momentan 5 verschiedene Werte. Gleich verteilt sind die allerdings überhaupt nicht.

  • Zu Beginn waren alle auf status = 0 (momentan 79.060.490)
  • Dann kommt die Verarbeitung. Während dieser sind sie auf status = 1 (und bleiben dort auch erst mal, falls bei der Verarbeitung etwas schief läuft). (momentan 655)
  • Wenn die Verarbeitung korrekt war, dann kommen sie auf status = 2 (momentan 148.110.039)
  • Wenn die Verarbeitung scheitert, dann wird es 7 bzw. 8 (je nach Fehler) (momentan 7: 14.320; 8: 11.478)

Da stimmt doch irgendwas nicht. So lange darf ein analyze nicht dauern.

Hm. schlecht. Was könnte da denn schief laufen?
Zum Hintergrund: Der Server an sich hat auf einer anderen DB im Moment gut zu tun und “autovacuum: VACUUM ANALYZE public.arbeitsplan (to prevent wraparound)” ist auch seit ca. 2 Tagen am Werk.

Noch zwei Fragen zum Schluss:
a) Was genau ist ein Bitmap Heap Scan?
b) Kann ich dem Query-Planer irgendwie mitteilen, dass er bitte zuerst einen Index Scan auf jobidindex machen soll und dann einen Filter auf status = 1?

Vielen Dank schon mal für Eure Unterstützung!
Beste Grüße
Jonathan

Ach richtig, das hatte ich übersehen. Sorry


autovacuum: VACUUM ANALYZE public.arbeitsplan (to prevent wraparound)" ist auch seit ca. 2 Tagen am Werk.

Ich bin absolut kein Experte für die VACUUM Thematik aber das sieht überhaupt nicht gut aus. Ich denke Du musst Deine autovacuum Einstellungen so ändern, dass deutlich häufiger aufgeräumt wird.

Kann ich dem Query-Planer irgendwie mitteilen, dass er bitte zuerst einen Index Scan auf jobidindex machen soll und dann einen Filter auf status = 1?

Die einzige Method die mir dabei einfällt ist ein “DROP INDEX statusindex”.
So wie die Verteilung der Daten ist, bringt der Index auf Status auch nicht wirklich viel.
Eventuell könntest Du einen partial index daraus machen, so dass nur Datensätze die nicht den Status 2 haben indiziert werden.

Hast du mal die wesentlichen Informationen bzgl. der Speichernutzung geprüft, also shared_buffers, work_mem, effective_cache_size usw.? Ggf. werden die Indexe deshalb nicht genutzt…

Hier stimmt was nicht:

              ->  Bitmap Index Scan on statusindex   (cost=0.00..18787.82 rows=641874 width=0) (actual  time=2956.954..2956.954 rows=1086709 loops=1)
                    Index Cond: (status = 1)

Das ist aus Deinem ersten Posting. Dieser Scan liefert, mit der Bedingung status=1, 1086709 Zeilen. Nun schreibst Du aber:

- Dann kommt die Verarbeitung. Während dieser sind sie auf status = 1 (und bleiben dort auch erst mal, falls bei der Verarbeitung etwas schief läuft). (momentan 655)

Andreas

Wo siehst Du, daß sie nicht genutzt werden?

Andreas

Hab mich auf die Aussagen des Postings verlassen.
Und bei 220 Mio Zeilen ist die Speicherfrage (liegt der Index im Hauptspeicher und wird genutzt; effective_cache_size) sicher nicht unrelevant, oder?

Ich verlasse mich auf die Aussage des Explains :wink:

Und bei 220 Mio Zeilen ist die Speicherfrage (liegt der Index im Hauptspeicher und wird genutzt; effective_cache_size) sicher nicht unrelevant, oder?

Sicher, aber beide Explains zeigen die Nutzung der Indexe. Nur unterschiedlich, je nachdem, wie selektiv die Indexe sind. Und da diese in einem Fall nicht wirklich selektiv sind (und vermutlich der Planner kurz vorm abkippen zu einem Seq-Scan ist), habe ich vorgeschlagen, den Index anders aufzubauen, so daß er selektiver (i hope …) wird.

Ach ja: bei 220 Millionen Einträgen und offensichtlich vielen UPDATEs drauf könnte es durchaus sein, daß da ein Index-Bloat - Problem besteht. Müßte man vielleicht auch mal untersuchen.

Andreas

Wenn ein ANALYZE bzw. VACUUM nach 2 Tagen immer noch nicht fertig ist…

Grundsätzliches:

Ein Index, der einen Großteil der Werte in der Tabelle umfasst, macht keinen Sinn. Die Datenbank wird diesen Index nicht nutzen, weil im Endeffekt sowieso viele Speicherseiten gelesen werden müssen. Also ist ein sequentieller Scan (bewiesenermaßen) günstiger. Aber ev. würde ein partieller Index auf die weniger vorhandenen Werte Sinn machen.

Per Default springt VACUUM erst bei 20% Änderungen an. Bei großen Tabellen ist das zu wenig und sollte manuell angepasst werden. Außerdem wird Autovacuum per Default ausgebremst, was bei großen Tabellen wiederum für lange Laufzeiten sorgt.


Wurde die Konfiguration dieser Datenbank überhaupt schon mal an die Arbeitssituation angepasst?

Vielen Dank für die vielen Antworten! Hier meine:

Hast du mal die wesentlichen Informationen bzgl. der Speichernutzung geprüft, also shared_buffers, work_mem, effective_cache_size usw.?



Wurde die Konfiguration dieser Datenbank überhaupt schon mal an die Arbeitssituation angepasst?

Ich bin nicht der Administrator der Maschine. Ich habe meine eigene Instanz und habe da drin alle Rechte, aber ich habe die nicht aufgesetzt. Insofern werde ich mich nochmal mit dem Administrator in Verbindung setzen. Der ist sehr hilfsbereit (siehe autovacuum unten) und hat mir schon mehr Ressourcen gegeben. Außerdem hat er mir die maximalen gleichzeitigen Verbindungen auf 800 hochgesetzt, so dass ich jetzt auch mit mehr als 200 Rechnern gleichzeitig die Daten verarbeiten kann. Die Maschine wurde aber aufgesetzt, ohne dass ich der Administration genau gesagt hatte, wie ich die Instanz nutzen will.
Die genannten Variablen habe ich jetzt mal über “select * from pg_settings” abgefragt:
shared_buffers: 1048576 unit: 8kB
work_mem: 65536 unit: kB
effective_cache_size: 16384 unit: 8kB

Info zur Maschine noch: Die ist halbwegs dick (ich glaube 32 Gig RAM und 8 Kerne), aber meine Instanz ist nicht die einzige da drauf.

Per Default springt VACUUM erst bei 20% Änderungen an. Bei großen Tabellen ist das zu wenig und sollte manuell angepasst werden. Außerdem wird Autovacuum per Default ausgebremst, was bei großen Tabellen wiederum für lange Laufzeiten sorgt.

Auf meine Frage, warum mich der Autovacuum so ausbremst, hat der Administrator den autovacuum-Dienst erst mal abgeschaltet (so dass er nur noch im Notfall läuft), da die Datenbank, wenn einmal alle Daten verarbeitet sind, nur noch zum Lesen verwendet wird. D.h. er meinte dass es reicht, wenn ich den vacuum mache, wenn wirklich der wraparound bevorsteht. (Vorerst bin ich der einzige Benutzer der Datenbank und ich würde natürlich vor dem Produktivbetrieb darin nochmal gründlich aufräumen (vaccum, analyze, clustered index, …).)
So wie ich Dich jetzt aber verstehe, wäre die gegenteilige Lösung besser. Was für Werte würdest Du denn vorschlagen?

Hier stimmt was nicht:
Das ist aus Deinem ersten Posting. Dieser Scan liefert, mit der Bedingung status=1, 1086709 Zeilen. Nun schreibst Du aber:

Ja, das passt natürlich nicht ganz zusammen, weil zwischen dem ersten Posting und dem späteren eine Runde Aufräumen lag; daher stand im ersten Posting auch

zwischen 1000 und 1 Mio status = 1

Die Aufräumarbeit hat nur leider am Query Plan auch nix geändert.
Hier die aktuelle Version. Die Zahlen, die ich immer noch nicht ganz verstehe, sind aber trotzdem komisch:

QUERY PLAN
Aggregate  (cost=20487.58..20487.59 rows=1 width=4) (actual time=2585.565..2585.566 rows=1 loops=1)
  ->  Bitmap Heap Scan on arbeitsplan  (cost=20139.64..20487.36 rows=87 width=4) (actual time=2584.293..2585.401 rows=655 loops=1)
        Recheck Cond: ((jobid = 2665390) AND (status = 1))
        ->  BitmapAnd  (cost=20139.64..20139.64 rows=87 width=0) (actual time=2581.127..2581.127 rows=0 loops=1)
              ->  Bitmap Index Scan on jobidindex  (cost=0.00..1351.52 rows=43273 width=0) (actual time=3.210..3.210 rows=7345 loops=1)
                    Index Cond: (jobid = 2665390)
              ->  Bitmap Index Scan on statusindex  (cost=0.00..18787.82 rows=641874 width=0) (actual time=2577.438..2577.438 rows=28000 loops=1)
                    Index Cond: (status = 1)
Total runtime: 2585.783 ms

Wenn ich das jetzt richtig verstanden habe, hast Du aus dem Feld, wo jetzt rows=28000 steht, geschlossen, dass 28000 den Status 1 haben. Das stimmt aber nicht, denn das Ergebnis von “select count(id) from arbeitsplan where status = 1” ist im Moment 655. (D.h. es gibt keine anderen als die von Job 2665390, den ich nach dem Aufräumen zum Test angeworfen habe.) Deutet das vielleicht darauf hin, dass mit meinem Index etwas nicht stimmt?

beide Explains zeigen die Nutzung der Indexe. Nur unterschiedlich, je nachdem, wie selektiv die Indexe sind.

Aber der explain bei status = 2 nutzt ja schlauerweise den jobidindex, der ziemlich selektiv ist und filtert danach nur noch auf status. Diese Option stünde ja bei status = 1 auch zur Verfügung.

Ich habe jetzt jedenfalls verstanden, dass der statusindex nix bringt, weil er eine zu niedrige Kardinalität hat. Was ich nicht verstehe ist, warum der Query Planer im Fall von status = 1 und status = 2 unterschiedliche Pläne wählt und sich für den schlechteren Plan bei status = 1 entscheidet. Das würde ich aber natürlich gerne verstehen, damit ich nicht wieder auf so einen Unsinn reinfalle.

Bevor ich den Index jetzt wegwerfe: Wie viel langsamer wird ein “select count(id) from arbeitsplan where status = 2” bzw “where status = 0”?

Und noch zu meiner Frage, was ein Bitmap Heap Scan ist:
(Andere Tabelle!)

QUERY PLAN
Bitmap Heap Scan on words  (cost=6.09..337.06 rows=84 width=288)
  Recheck Cond: (((wordform)::text = 'the'::text) AND ((pos)::text = 'DT'::text))
  ->  Bitmap Index Scan on wordform_pos  (cost=0.00..6.07 rows=84 width=0)
        Index Cond: (((wordform)::text = 'the'::text) AND ((pos)::text = 'DT'::text))

Warum passiert da kein Index Scan? In diesem Fall gibt es einen unique Index auf wordform und pos gemeinsam. Wie genau nutzt ein Bitmap Heap Scan den Index?


Nochmal ganz herzlichen Dank für die Anregungen! Ich glaube, ich komme der Sache langsam näher.

Woher weißt du, ob der Plan mit dem Index für status = 1 wirklich schlechter ist/wäre?
Kannst du das anhand von Zahlen beweisen oder denkst du nur, das ein verwendeter Index in jedem Fall die Sache beschleunigen sollte?


Wenn ich mir die 3 Settings anschaue, ist shared_buffers zumindest angepasst worden. Allerdings ist die Einstellung mit 8 GB wohl vielleicht sogar zu groß gewählt. Müsste man sich die Performance der Maschine anschauen.
Bei 64 MB work_mem müsste man auch mal schauen, ob es das wirklich braucht. Aber das passt fürs erste.
effective_cache_size ist auf jeden Fall zu gering eingestellt - das Betriebssystem hat mit Sicherheit mehr als 128 MB Speicher frei.
Da ist also noch viel zu konfigurieren …


Zum Autovacuum: wieso schaltet ihr das aus, wenn ihr nicht wisst, was genau das bewirkt?
Nun habt ihr das Problem, das PostgreSQL die Tabellen zu einem ungünstigen Zeitpunkt trotzdem durchsucht (daher kommt das “to prevent wraparound”).


Ich glaube ehrlich gesagt, dass du der Sache nicht wirklich “näher” kommst sondern dass ihr gerade die Probleme erledigt, die gerade auftreten :wink:

Woher weißt du, ob der Plan mit dem Index für status = 1 wirklich schlechter ist/wäre?
Kannst du das anhand von Zahlen beweisen oder denkst du nur, das ein verwendeter Index in jedem Fall die Sache beschleunigen sollte?

Hm. Jetzt bin ich vollends verwirrt. Wenn Du Dir die ersten beiden explain analyze anschaust (ganz oben, erstes Posting), dann habe ich das erste bislang so verstanden:
Er sucht jobidindex alle, die jobid = 2665390 haben und filtert die auf status = 2 (D.h. ich dachte, er schaut alle an, die jobid 2665390 haben, um diejenigen zu entdecken, die status = 2 haben.)
Dieses Vorgehen ist fast um den Faktor 200 schneller als der Query Plan für jobid = 2665390 und status = 1, obwohl er in meinem naiven Verständnis auch wieder jobid = 2665390 im jobindex suchen könnte und diese dann alle auf status = 1 überprüfen könnte.
Habe ich diesen Query Plan falsch verstanden und das läuft anders ab?

Da ist also noch viel zu konfigurieren …

Vielen Dank für die Tipps! Ich werde das weitergeben.

Zum Autovacuum: wieso schaltet ihr das aus, wenn ihr nicht wisst, was genau das bewirkt?
Nun habt ihr das Problem, das PostgreSQL die Tabellen zu einem ungünstigen Zeitpunkt trotzdem durchsucht (daher kommt das “to prevent wraparound”)

Wir wussten durchaus, was das tut. Auf die Tour kommt er aber halt nicht schon bei 20% daher, sondern erst, wenn es wirklich notwendig ist. Dadurch besteht weniger häufig die Notwendigkeit, manuell einzugreifen.
Kurze Erklärung zum Hintergrund: Wenn autovacuum läuft, schafft der Server es nicht, die Daten von mehr als 50 Jobs, die gleichzeitig Arbeit abrufen und geleistete Arbeit wieder zurückschreiben, abzuarbeiten. Wenn kein autovacuum läuft, dann kann er ungefähr 200 Jobs parallel bedienen, ohne dass die DB zum Flaschenhals wird. Wenn also der autovacuum zuschlägt kann es passieren, dass ich einen Anruf von der Clusterverwaltung bekomme, der mich freundlich darauf hinweist, dass ich gerade 150 CPUs blockiere und die offensichtlich alle nix zu tun haben. D.h. die Datenbankkonfiguration muss hier auf andere Faktoren abgestimmt werden.

Beste Grüße
Jonathan

150 CPUs? Clusterverwaltung? Anruf? Meinst du nicht, es wäre Zeit, sich das gesamte System mal von vorn bis hinten anzuschauen und alle Schwachstellen auszubügeln?
Bisher rätselt ihr doch eher an den Symptomen herum, oder?


Zum Autovacuum: das kann man ausbremsen. Durch das Abschalten von Autovacuum habt ihr die Gesamtperformance jedoch eher verschlechtert als verbessert.
Grund: die Tabellen wachsen soweit an bis das Wraparound Vacuum zuschlägt. Dadurch fragmentiert ihr die Tabellen unnötig stark. In der Folge müssen wesentlich mehr Blocks als notwendig gelesen werden, um die gleiche Anzahl Daten in den Speicher zu laden. Man kann Autovacuum auch so konfigurieren, dass es mit dem Gesamtsystem harmoniert. Dazu ist aber ein Abstimmen auf die Anwendung notwendig, dafür gibt es kein allgemeingültiges Rezept.

Es gibt auch noch weitere Mittel und Wege, ggf. die Performance so eines Systems zu erhöhen. Die sind jedoch alle davon abhängig, was genau die Anwendung da treibt.


In deinem ersten Abfrageplan wird status = 2 nicht von einem Index bedient. Wenn ich dich richtig verstehe, möchtest du das aber erreichen, oder?


Oh, zu deinem gestarteten ANALYZE Aufruf: kann es sein, dass der noch in der Warteschleife hängt? Was sagt denn die “waiting” Spalte in “pg_stat_activity”? Es ist gut möglich, dass das Vacuum den ANALYZE Aufruf blockiert.

Meinst du nicht, es wäre Zeit, sich das gesamte System mal von vorn bis hinten anzuschauen und alle Schwachstellen auszubügeln?

Naja, im Moment läuft das Projekt nebenbei, ohne Etat und ohne Leute. Das machen ein Kollege und ich und das Rechenzentrum hilft uns netterweise, indem es uns Infrastruktur zur Verfügung stellt.
Das System ist ja im Moment gerade in der Entwicklung und der Schritt, der gerade passiert, ist an sich sehr einfach. Ich erzähle gerne mehr darüber:
Die Datenbank enthält ca. 220 Mio Sätze, die automatisch analysiert werden sollen. Dieser Vorgang ist sehr zeitaufwändig und muss auf ein Cluster verteilt werden. Die Tabelle arbeitsplan macht die Organisation. Jeder Rechner aus dem Cluster startet einen Preprocessor. Dieser meldet sich bei der Datenbank, greift sich ein Paket von 2000 Sätzen (d.h. select for update) und setzt diese auf status = 1, schreibt die jobid rein und welche Position der Satz in der Warteschlange hat. Dann übergibt er die Sätze an das eigentliche Analyseprogramm. Dieses übergibt die analysierten Sätze an den Postprocessor, welcher in der Datenbank die zu jobid, status und reihenfolge (=position in der Warteschlange) gehörende id raussucht und eine komprimierte Version der Analysen unter dieser id in eine andere Tabelle schreibt. Dann wird status für diesen Satz auf 2 gesetzt. Der Preprocessor schaut regelmäßig nach, wie viele mit der aktuellen jobid und status = 1 noch in der DB sind, und wenn dieser Wert unter 500 fällt, dann holt er sich die nächsten 2000 Sätze. Und das ist eben die Anfrage, die komisch war.

Zum Autovacuum: das kann man ausbremsen. Durch das Abschalten von Autovacuum habt ihr die Gesamtperformance jedoch eher verschlechtert als verbessert.
Grund: die Tabellen wachsen soweit an bis das Wraparound Vacuum zuschlägt. Dadurch fragmentiert ihr die Tabellen unnötig stark. In der Folge müssen wesentlich mehr Blocks als notwendig gelesen werden, um die gleiche Anzahl Daten in den Speicher zu laden.

Das ist sehr interessant. Da werden wir uns auf jeden Fall drum kümmern. Vielen Dank für die Erklärung! (Ich nehme Ratschläge besonders gerne an, wenn ich sie auch verstanden habe :slight_smile: )

In deinem ersten Abfrageplan wird status = 2 nicht von einem Index bedient. Wenn ich dich richtig verstehe, möchtest du das aber erreichen, oder?

Oh, da habe ich mich unklar ausgedrückt. Bei status = 2 will ich nichts ändern, denn diese Abfrage geht schnell genug. Ich will erreichen, dass die gleiche Abfrage mit status = 1 genauso schnell geht. Und ich vermute, sie müsste genauso schnell gehen, wenn sie den gleichen Query Plan verwenden würde. (vgl. meine Interpretation in meinem Post eins weiter oben.)

Oh, zu deinem gestarteten ANALYZE Aufruf: kann es sein, dass der noch in der Warteschleife hängt? Was sagt denn die “waiting” Spalte in “pg_stat_activity”? Es ist gut möglich, dass das Vacuum den ANALYZE Aufruf blockiert.

In der Tat, Du hast recht! (phppgadmin zeigt standardmäßig die Spalte “waiting” nicht an.) Das erklärt es dann. D.h. warten bis der autovacuum fertig ist und dann nochmal die Serverparameter anpassen.

Ganz herzlichen Dank für die ausführliche Analyse schon mal!
Gruß
Jonathan

Dein Anfrageplan mit status = 1 wird nicht so schnell werden, weil der Index nicht selektiv genug ist. Du kannst das gern probieren, indem du auf einer psql Shell (vergiss phppgadmin) mal mit SET enable_* herumspielst.Da kann man die Nutzung von Indexes forcieren, indem man z. B. sagt: “SET enable_seqscan = off”. Es würde mich aber wundern, wenn der Anfrageplan hinterher besser aussieht als vorher - und auch schneller läuft.

Das war ein guter Tipp! Der andere Plan ist tatsächlich schneller, wie ich vermutet habe.

Zum Vergleich:
explain analyze select count(id) from arbeitsplan where status = 1 and jobid = 2665390

QUERY PLAN
Aggregate  (cost=20487.58..20487.59 rows=1 width=4) (actual time=2485.244..2485.245 rows=1 loops=1)
  ->  Bitmap Heap Scan on arbeitsplan  (cost=20139.64..20487.36 rows=87 width=4) (actual time=2483.974..2485.090 rows=655 loops=1)
        Recheck Cond: ((jobid = 2665390) AND (status = 1))
        ->  BitmapAnd  (cost=20139.64..20139.64 rows=87 width=0) (actual time=2480.682..2480.682 rows=0 loops=1)
              ->  Bitmap Index Scan on jobidindex  (cost=0.00..1351.52 rows=43273 width=0) (actual time=2.704..2.704 rows=7345 loops=1)
                    Index Cond: (jobid = 2665390)
              ->  Bitmap Index Scan on statusindex  (cost=0.00..18787.82 rows=641874 width=0) (actual time=2477.646..2477.646 rows=28000 loops=1)
                    Index Cond: (status = 1)
Total runtime: 2485.427 ms

set enable_bitmapscan=off;explain analyze select count(id) from arbeitsplan where status = 1 and jobid = 2665390

QUERY PLAN
Aggregate  (cost=87391.83..87391.84 rows=1 width=4) (actual  time=14.644..14.645 rows=1 loops=1)
  ->  Index Scan using jobidindex on arbeitsplan   (cost=0.00..87391.61 rows=87 width=4) (actual time=5.121..14.407  rows=655 loops=1)
        Index Cond: (jobid = 2665390)
        Filter: (status = 1)
Total runtime: 14.818 ms

Die Frage ist damit weiterhin: Warum entscheidet sich der Query Planer für die offensichtlich schlechtere Version? Und wie kann ich das ändern, ohne hacks der Art “set enable_bitmapscan=off”?

Beste Grüße
Jonathan