Wie kann die ORDER BY-Sortierung beschleunigt werden, wenn der GIN-Index in PostgreSQL verwendet wird?

12

Ich habe einen Tisch wie diesen:

CREATE TABLE products (
  id serial PRIMARY KEY, 
  category_ids integer[],
  published boolean NOT NULL,
  score integer NOT NULL,
  title varchar NOT NULL);

Ein Produkt kann mehreren Kategorien angehören. category_idsDie Spalte enthält eine Liste der IDs aller Produktkategorien.

Eine typische Abfrage sieht folgendermaßen aus (immer auf der Suche nach einer einzelnen Kategorie):

SELECT * FROM products WHERE published
  AND category_ids @> ARRAY[23465]
ORDER BY score DESC, title
LIMIT 20 OFFSET 8000;

Um es zu beschleunigen, benutze ich den folgenden Index:

CREATE INDEX idx_test1 ON products
  USING GIN (category_ids gin__int_ops) WHERE published;

Dieser hilft sehr, es sei denn, es gibt zu viele Produkte in einer Kategorie. Es filtert schnell Produkte heraus, die zu dieser Kategorie gehören, aber dann gibt es eine Sortieroperation, die auf die harte Tour (ohne Index) durchgeführt werden muss.

Eine installierte btree_ginErweiterung, mit der ich einen mehrspaltigen GIN-Index wie folgt erstellen kann:

CREATE INDEX idx_test2 ON products USING GIN (
  category_ids gin__int_ops, score, title) WHERE published;

Aber Postgres will das nicht zum Sortieren verwenden . Auch wenn ich den DESCBezeichner in der Abfrage entferne .

Alle alternativen Ansätze zur Optimierung der Aufgabe sind sehr willkommen.


Zusätzliche Information:

  • PostgreSQL 9.4 mit Intarray-Erweiterung
  • Die Gesamtzahl der Produkte beträgt derzeit 260.000, es wird jedoch ein deutliches Wachstum erwartet (bis zu 10 Millionen, dies ist eine mandantenfähige E-Commerce-Plattform).
  • Produkte pro Kategorie 1..10000 (kann bis zu 100.000 wachsen), der Durchschnitt liegt unter 100, aber diese Kategorien mit einer großen Anzahl von Produkten ziehen tendenziell viel mehr Anfragen an

Der folgende Abfrageplan wurde von einem kleineren Testsystem erhalten (4680 Produkte in der ausgewählten Kategorie, insgesamt 200.000 Produkte in der Tabelle):

Limit  (cost=948.99..948.99 rows=1 width=72) (actual time=82.330..82.341 rows=20 loops=1)
  ->  Sort  (cost=948.37..948.99 rows=245 width=72) (actual time=80.231..81.337 rows=4020 loops=1)
        Sort Key: score, title
        Sort Method: quicksort  Memory: 928kB
        ->  Bitmap Heap Scan on products  (cost=13.90..938.65 rows=245 width=72) (actual time=1.919..16.044 rows=4680 loops=1)
              Recheck Cond: ((category_ids @> '{292844}'::integer[]) AND published)
              Heap Blocks: exact=3441
              ->  Bitmap Index Scan on idx_test2  (cost=0.00..13.84 rows=245 width=0) (actual time=1.185..1.185 rows=4680 loops=1)
                    Index Cond: (category_ids @> '{292844}'::integer[])
Planning time: 0.202 ms
Execution time: 82.404 ms

Hinweis Nr. 1 : 82 ms sehen möglicherweise nicht so beängstigend aus, aber das liegt daran, dass der Sortierpuffer in den Speicher passt. Sobald ich alle Spalten aus der Produkttabelle ausgewählt habe ( SELECT * FROM ...und im wirklichen Leben gibt es ungefähr 60 Spalten), wird Sort Method: external merge Disk: 5696kBdie Ausführungszeit verdoppelt. Und das nur für 4680 Produkte.

Aktionspunkt Nr. 1 (stammt aus Anmerkung Nr. 1): Um den Speicherbedarf des Sortiervorgangs zu verringern und ihn daher ein wenig zu beschleunigen, ist es ratsam, zuerst Produkt-IDs abzurufen, zu sortieren und zu begrenzen und dann vollständige Datensätze abzurufen:

SELECT * FROM products WHERE id IN (
  SELECT id FROM products WHERE published AND category_ids @> ARRAY[23465]
  ORDER BY score DESC, title LIMIT 20 OFFSET 8000
) ORDER BY score DESC, title;

Dies bringt uns zurück zu Sort Method: quicksort Memory: 903kBund ~ 80 ms für 4680 Produkte. Kann immer noch langsam sein, wenn die Anzahl der Produkte auf 100.000 steigt.

Jaroslaw Stavnichiy
quelle
Auf dieser Seite: hlinnaka.iki.fi/2014/03/28/… gibt es einen Kommentar, dass btree_gin nicht zum Sortieren verwendet werden kann.
Mladen Uzelac
OK, ich habe den Titel umformuliert, um mehr Optionen zuzulassen.
Jaroslaw Stavnichiy
Suchen Sie immer nach einer einzelnen Kategorie? Und bitte geben Sie einige weitere grundlegende Informationen an: Postgres-Version, Kardinalitäten, Zeilen pro Kategorie (min / avg / max). Beachten Sie die Anweisungen in den Tag-Informationen für die Postgresql-Leistung . Und: scorekann NULL sein, aber Sie sortieren trotzdem nach score DESC, nicht score DESC NULLS LAST. Der eine oder andere scheint nicht richtig zu sein ...
Erwin Brandstetter
Ich habe zusätzliche Informationen wie gewünscht hinzugefügt. Ich bin immer auf der Suche nach einer einzelnen Kategorie. Und scoretatsächlich ist NICHT NULL - ich habe die Tabellendefinition korrigiert.
Jaroslaw Stavnichiy

Antworten:

9

Ich habe viel experimentiert und hier sind meine Ergebnisse.

GIN und Sortieren

Der GIN-Index (ab Version 9.4) kann die Bestellung nicht unterstützen .

Von den derzeit von PostgreSQL unterstützten Indextypen kann nur der B-Baum eine sortierte Ausgabe erzeugen. Die anderen Indextypen geben übereinstimmende Zeilen in einer nicht angegebenen, implementierungsabhängigen Reihenfolge zurück.

work_mem

Vielen Dank an Chris für den Hinweis auf diesen Konfigurationsparameter . Der Standardwert ist 4 MB. Wenn Ihr Recordset größer ist, kann das Erhöhen work_memauf den richtigen Wert (zu finden unter EXPLAIN ANALYSE) die Sortiervorgänge erheblich beschleunigen.

ALTER SYSTEM SET work_mem TO '32MB';

Starten Sie den Server neu, damit die Änderung wirksam wird, und überprüfen Sie dann Folgendes:

SHOW work_mem;

Ursprüngliche Abfrage

Ich habe meine Datenbank mit 650.000 Produkten gefüllt, wobei einige Kategorien bis zu 40.000 Produkte enthalten. Ich habe die Abfrage etwas vereinfacht, indem ich die folgende publishedKlausel entfernt habe:

SELECT * FROM products WHERE category_ids @> ARRAY [248688]
ORDER BY score DESC, title LIMIT 10 OFFSET 30000;

Limit  (cost=2435.62..2435.62 rows=1 width=1390) (actual time=1141.254..1141.256 rows=10 loops=1)
  ->  Sort  (cost=2434.00..2435.62 rows=646 width=1390) (actual time=1115.706..1140.513 rows=30010 loops=1)
        Sort Key: score, title
        Sort Method: external merge  Disk: 29656kB
        ->  Bitmap Heap Scan on products  (cost=17.01..2403.85 rows=646 width=1390) (actual time=11.831..25.646 rows=41666 loops=1)
              Recheck Cond: (category_ids @> '{248688}'::integer[])
              Heap Blocks: exact=6471
              ->  Bitmap Index Scan on idx_products_category_ids_gin  (cost=0.00..16.85 rows=646 width=0) (actual time=10.140..10.140 rows=41666 loops=1)
                    Index Cond: (category_ids @> '{248688}'::integer[])
Planning time: 0.288 ms
Execution time: 1146.322 ms

Wie wir sehen können, work_memwar dies nicht genug Sort Method: external merge Disk: 29656kB(die Anzahl hier ist ungefähr, es werden etwas mehr als 32 MB für In-Memory-QuickSort benötigt).

Reduzieren Sie den Speicherbedarf

Wählen Sie keine vollständigen Datensätze zum Sortieren aus, verwenden Sie keine IDs, wenden Sie Sortieren, Versetzen und Begrenzen an und laden Sie dann nur 10 Datensätze, die wir benötigen:

SELECT * FROM products WHERE id in (
  SELECT id FROM products WHERE category_ids @> ARRAY[248688]
  ORDER BY score DESC, title LIMIT 10 OFFSET 30000
) ORDER BY score DESC, title;

Sort  (cost=2444.10..2444.11 rows=1 width=1390) (actual time=707.861..707.862 rows=10 loops=1)
  Sort Key: products.score, products.title
  Sort Method: quicksort  Memory: 35kB
  ->  Nested Loop  (cost=2436.05..2444.09 rows=1 width=1390) (actual time=707.764..707.803 rows=10 loops=1)
        ->  HashAggregate  (cost=2435.63..2435.64 rows=1 width=4) (actual time=707.744..707.746 rows=10 loops=1)
              Group Key: products_1.id
              ->  Limit  (cost=2435.62..2435.62 rows=1 width=72) (actual time=707.732..707.734 rows=10 loops=1)
                    ->  Sort  (cost=2434.00..2435.62 rows=646 width=72) (actual time=704.163..706.955 rows=30010 loops=1)
                          Sort Key: products_1.score, products_1.title
                          Sort Method: quicksort  Memory: 7396kB
                          ->  Bitmap Heap Scan on products products_1  (cost=17.01..2403.85 rows=646 width=72) (actual time=11.587..35.076 rows=41666 loops=1)
                                Recheck Cond: (category_ids @> '{248688}'::integer[])
                                Heap Blocks: exact=6471
                                ->  Bitmap Index Scan on idx_products_category_ids_gin  (cost=0.00..16.85 rows=646 width=0) (actual time=9.883..9.883 rows=41666 loops=1)
                                      Index Cond: (category_ids @> '{248688}'::integer[])
        ->  Index Scan using products_pkey on products  (cost=0.42..8.45 rows=1 width=1390) (actual time=0.004..0.004 rows=1 loops=10)
              Index Cond: (id = products_1.id)
Planning time: 0.682 ms
Execution time: 707.973 ms

Hinweis Sort Method: quicksort Memory: 7396kB. Ergebnis ist viel besser.

JOIN und zusätzlicher B-Tree-Index

Wie Chris empfohlen hat, habe ich einen zusätzlichen Index erstellt:

CREATE INDEX idx_test7 ON products (score DESC, title);

Zuerst habe ich versucht, so beizutreten:

SELECT * FROM products NATURAL JOIN
  (SELECT id FROM products WHERE category_ids @> ARRAY[248688]
  ORDER BY score DESC, title LIMIT 10 OFFSET 30000) c
ORDER BY score DESC, title;

Der Abfrageplan unterscheidet sich geringfügig, das Ergebnis ist jedoch dasselbe:

Sort  (cost=2444.10..2444.11 rows=1 width=1390) (actual time=700.747..700.747 rows=10 loops=1)
  Sort Key: products.score, products.title
  Sort Method: quicksort  Memory: 35kB
  ->  Nested Loop  (cost=2436.05..2444.09 rows=1 width=1390) (actual time=700.651..700.690 rows=10 loops=1)
        ->  HashAggregate  (cost=2435.63..2435.64 rows=1 width=4) (actual time=700.630..700.630 rows=10 loops=1)
              Group Key: products_1.id
              ->  Limit  (cost=2435.62..2435.62 rows=1 width=72) (actual time=700.619..700.619 rows=10 loops=1)
                    ->  Sort  (cost=2434.00..2435.62 rows=646 width=72) (actual time=697.304..699.868 rows=30010 loops=1)
                          Sort Key: products_1.score, products_1.title
                          Sort Method: quicksort  Memory: 7396kB
                          ->  Bitmap Heap Scan on products products_1  (cost=17.01..2403.85 rows=646 width=72) (actual time=10.796..32.258 rows=41666 loops=1)
                                Recheck Cond: (category_ids @> '{248688}'::integer[])
                                Heap Blocks: exact=6471
                                ->  Bitmap Index Scan on idx_products_category_ids_gin  (cost=0.00..16.85 rows=646 width=0) (actual time=9.234..9.234 rows=41666 loops=1)
                                      Index Cond: (category_ids @> '{248688}'::integer[])
        ->  Index Scan using products_pkey on products  (cost=0.42..8.45 rows=1 width=1390) (actual time=0.004..0.004 rows=1 loops=10)
              Index Cond: (id = products_1.id)
Planning time: 1.015 ms
Execution time: 700.918 ms

Durch das Spielen mit verschiedenen Offsets und Produktzählungen konnte PostgreSQL keinen zusätzlichen B-Tree-Index verwenden.

Also bin ich klassisch gegangen und habe einen Kreuzungstisch erstellt :

CREATE TABLE prodcats AS SELECT id AS product_id, unnest(category_ids) AS category_id FROM products;
CREATE INDEX idx_prodcats_cat_prod_id ON prodcats (category_id, product_id);

SELECT p.* FROM products p JOIN prodcats c ON (p.id=c.product_id)
WHERE c.category_id=248688
ORDER BY p.score DESC, p.title LIMIT 10 OFFSET 30000;

Limit  (cost=122480.06..122480.09 rows=10 width=1390) (actual time=1290.360..1290.362 rows=10 loops=1)
  ->  Sort  (cost=122405.06..122509.00 rows=41574 width=1390) (actual time=1264.250..1289.575 rows=30010 loops=1)
        Sort Key: p.score, p.title
        Sort Method: external merge  Disk: 29656kB
        ->  Merge Join  (cost=50.46..94061.13 rows=41574 width=1390) (actual time=117.746..182.048 rows=41666 loops=1)
              Merge Cond: (p.id = c.product_id)
              ->  Index Scan using products_pkey on products p  (cost=0.42..90738.43 rows=646067 width=1390) (actual time=0.034..116.313 rows=210283 loops=1)
              ->  Index Only Scan using idx_prodcats_cat_prod_id on prodcats c  (cost=0.43..1187.98 rows=41574 width=4) (actual time=0.022..7.137 rows=41666 loops=1)
                    Index Cond: (category_id = 248688)
                    Heap Fetches: 0
Planning time: 0.873 ms
Execution time: 1294.826 ms

Da der B-Tree-Index immer noch nicht verwendet wurde, passte die work_memErgebnismenge nicht , daher schlechte Ergebnisse.

Unter bestimmten Umständen entscheidet sich PostgreSQL aufgrund der großen Anzahl von Produkten und des kleinen Offsets für die Verwendung des B-Tree-Index:

SELECT p.* FROM products p JOIN prodcats c ON (p.id=c.product_id)
WHERE c.category_id=248688
ORDER BY p.score DESC, p.title LIMIT 10 OFFSET 300;

Limit  (cost=3986.65..4119.51 rows=10 width=1390) (actual time=264.176..264.574 rows=10 loops=1)
  ->  Nested Loop  (cost=0.98..552334.77 rows=41574 width=1390) (actual time=250.378..264.558 rows=310 loops=1)
        ->  Index Scan using idx_test7 on products p  (cost=0.55..194665.62 rows=646067 width=1390) (actual time=0.030..83.026 rows=108037 loops=1)
        ->  Index Only Scan using idx_prodcats_cat_prod_id on prodcats c  (cost=0.43..0.54 rows=1 width=4) (actual time=0.001..0.001 rows=0 loops=108037)
              Index Cond: ((category_id = 248688) AND (product_id = p.id))
              Heap Fetches: 0
Planning time: 0.585 ms
Execution time: 264.664 ms

Dies ist in der Tat ziemlich logisch, da der B-Baum-Index hier kein direktes Ergebnis liefert, sondern nur als Leitfaden für den sequentiellen Scan verwendet wird.

Vergleichen wir mit der GIN-Abfrage:

SELECT * FROM products WHERE id in (
  SELECT id FROM products WHERE category_ids @> ARRAY[248688]
  ORDER BY score DESC, title LIMIT 10 OFFSET 300
) ORDER BY score DESC, title;

Sort  (cost=2519.53..2519.55 rows=10 width=1390) (actual time=143.809..143.809 rows=10 loops=1)
  Sort Key: products.score, products.title
  Sort Method: quicksort  Memory: 35kB
  ->  Nested Loop  (cost=2435.14..2519.36 rows=10 width=1390) (actual time=143.693..143.736 rows=10 loops=1)
        ->  HashAggregate  (cost=2434.71..2434.81 rows=10 width=4) (actual time=143.678..143.680 rows=10 loops=1)
              Group Key: products_1.id
              ->  Limit  (cost=2434.56..2434.59 rows=10 width=72) (actual time=143.668..143.670 rows=10 loops=1)
                    ->  Sort  (cost=2433.81..2435.43 rows=646 width=72) (actual time=143.642..143.653 rows=310 loops=1)
                          Sort Key: products_1.score, products_1.title
                          Sort Method: top-N heapsort  Memory: 68kB
                          ->  Bitmap Heap Scan on products products_1  (cost=17.01..2403.85 rows=646 width=72) (actual time=11.625..31.868 rows=41666 loops=1)
                                Recheck Cond: (category_ids @> '{248688}'::integer[])
                                Heap Blocks: exact=6471
                                ->  Bitmap Index Scan on idx_products_category_ids_gin  (cost=0.00..16.85 rows=646 width=0) (actual time=9.916..9.916 rows=41666 loops=1)
                                      Index Cond: (category_ids @> '{248688}'::integer[])
        ->  Index Scan using products_pkey on products  (cost=0.42..8.45 rows=1 width=1390) (actual time=0.004..0.004 rows=1 loops=10)
              Index Cond: (id = products_1.id)
Planning time: 0.630 ms
Execution time: 143.921 ms

Das Ergebnis von GIN ist viel besser. Ich überprüfte mit verschiedenen Kombinationen von Anzahl der Produkte und Offset, unter keinen Umständen war der Ansatz der Kreuzungstabelle besser .

Die Kraft des realen Index

Damit PostgreSQL den Index zum Sortieren vollständig nutzen kann, müssen sich alle Abfrageparameter WHEREsowie ORDER BYParameter in einem einzelnen B-Baum-Index befinden. Dazu habe ich Sortierfelder vom Produkt in die Junction-Tabelle kopiert:

CREATE TABLE prodcats AS SELECT id AS product_id, unnest(category_ids) AS category_id, score, title FROM products;
CREATE INDEX idx_prodcats_1 ON prodcats (category_id, score DESC, title, product_id);

SELECT * FROM products WHERE id in (SELECT product_id FROM prodcats WHERE category_id=248688 ORDER BY score DESC, title LIMIT 10 OFFSET 30000) ORDER BY score DESC, title;

Sort  (cost=2149.65..2149.67 rows=10 width=1390) (actual time=7.011..7.011 rows=10 loops=1)
  Sort Key: products.score, products.title
  Sort Method: quicksort  Memory: 35kB
  ->  Nested Loop  (cost=2065.26..2149.48 rows=10 width=1390) (actual time=6.916..6.950 rows=10 loops=1)
        ->  HashAggregate  (cost=2064.83..2064.93 rows=10 width=4) (actual time=6.902..6.904 rows=10 loops=1)
              Group Key: prodcats.product_id
              ->  Limit  (cost=2064.02..2064.71 rows=10 width=74) (actual time=6.893..6.895 rows=10 loops=1)
                    ->  Index Only Scan using idx_prodcats_1 on prodcats  (cost=0.56..2860.10 rows=41574 width=74) (actual time=0.010..6.173 rows=30010 loops=1)
                          Index Cond: (category_id = 248688)
                          Heap Fetches: 0
        ->  Index Scan using products_pkey on products  (cost=0.42..8.45 rows=1 width=1390) (actual time=0.003..0.003 rows=1 loops=10)
              Index Cond: (id = prodcats.product_id)
Planning time: 0.318 ms
Execution time: 7.066 ms

Und dies ist das schlimmste Szenario mit einer großen Anzahl von Produkten in der ausgewählten Kategorie und einem großen Offset. Bei Offset = 300 beträgt die Ausführungszeit nur 0,5 ms.

Leider erfordert die Wartung eines solchen Verbindungstisches zusätzlichen Aufwand. Dies könnte über indizierte materialisierte Ansichten erreicht werden. Dies ist jedoch nur dann nützlich, wenn Ihre Daten selten aktualisiert werden, da das Aktualisieren einer solchen materialisierten Ansicht ein ziemlich schwerer Vorgang ist.

Daher bleibe ich bisher beim GIN-Index mit einer erhöhten work_memund reduzierten Speicherabfrage.

Jaroslaw Stavnichiy
quelle
Sie nicht brauchen Neustart für eine Änderung der allgemeinen work_memEinstellung in postgresql.conf. Nachladen ist genug. Und lassen Sie mich davor warnen, work_memin einer Mehrbenutzerumgebung global zu hoch einzustellen (auch nicht zu niedrig). Wenn Sie einige Abfragen haben, die mehr benötigen work_mem, stellen Sie sie für die Sitzung nur mit SET- oder nur für die Transaktion mit höher ein SET LOCAL. Siehe: dba.stackexchange.com/a/48633/3684
Erwin Brandstetter
Was für eine großartige Antwort. Hat mir sehr geholfen, insbesondere bei der Sortierung der Festplatte -> In-Memory-Sortierung, schnelle Änderung für einen großartigen Gewinn, danke!
Ricardo Villamil
4

Hier sind einige schnelle Tipps, die Ihnen helfen können, Ihre Leistung zu verbessern. Ich beginne mit dem einfachsten Tipp, der für Sie fast mühelos ist, und gehe nach dem ersten zum schwierigeren Tipp über.

1. work_mem

Ich sehe also sofort, dass eine in Ihrem Erklärungsplan angegebene Sorte Sort Method: external merge Disk: 5696kBweniger als 6 MB verbraucht, aber auf die Festplatte verschüttet wird. Sie müssen Ihre work_memEinstellung in Ihrer postgresql.confDatei erhöhen , damit die Sortierung in den Speicher passt.

BEARBEITEN: Bei weiterer Prüfung catgory_idsstelle ich außerdem fest, dass der Bitmap-Index-Scan nach Verwendung des Index zur Überprüfung, ob er Ihren Kriterien entspricht, "verlustbehaftet" werden muss und die Bedingung beim Lesen der Zeilen auf den entsprechenden Heap-Seiten erneut überprüfen muss . In diesem Beitrag auf postgresql.org finden Sie eine bessere Erklärung als ich angegeben habe. : P Der Hauptpunkt ist, dass Ihrwork_mem viel zu niedrig sind. Wenn Sie die Standardeinstellungen auf Ihrem Server nicht angepasst haben, funktioniert sie nicht gut.

Für dieses Update benötigen Sie im Wesentlichen keine Zeit. Eine Änderung zu postgresql.conf, und Sie sind weg! Weitere Informationen finden Sie auf dieser Seite zur LeistungsoptimierungWeitere Tipps finden zur .

2. Schemaänderung

Sie haben also in Ihrem Schemadesign die Entscheidung getroffen, das category_idsin ein ganzzahliges Array zu denormalisieren , wodurch Sie gezwungen werden, einen GIN- oder GIST-Index zu verwenden, um schnellen Zugriff zu erhalten. Nach meiner Erfahrung ist Ihre Auswahl eines GIN-Index für Lesevorgänge schneller als für einen GIST. In diesem Fall haben Sie also die richtige Wahl getroffen. GIN ist jedoch ein unsortierter Index. denken sie eher wie ein Schlüssel-Wert, wo Gleichheit Prädikate sind leicht zu überprüfen, aber Operationen wie WHERE >, WHERE <oderORDER BY nicht durch den Index erleichtert.

Ein vernünftiger Ansatz wäre, Ihr Design mithilfe eines Brückentisches / Junction-Tisches zu normalisieren , mit der viele-zu-viele-Beziehungen in Datenbanken angegeben werden.

In diesem Fall haben Sie viele Kategorien und eine Reihe entsprechender Ganzzahlen category_id, und Sie haben viele Produkte und die entsprechenden Ganzzahlen product_id. category_idEntfernen Sie anstelle einer Spalte in Ihrer Produkttabelle, die ein ganzzahliges Array von s ist, diese Array-Spalte aus Ihrem Schema und erstellen Sie eine Tabelle als

CREATE TABLE join_products_categories (product_id int, category_id int);

Anschließend können Sie B-Tree-Indizes für die beiden Spalten der Brückentabelle generieren.

CREATE INDEX idx_products_in_join_table ON join_products_categories (product_id);
CREATE INDEX idx_products_in_join_table ON join_products_categories (category_id);

Nur meine bescheidene Meinung, aber diese Änderungen können einen großen Unterschied für Sie machen. Probieren Sie das auswork_mem Änderung zumindest als erstes aus.

Viel Glück!

BEARBEITEN:

Erstellen Sie einen zusätzlichen Index, um das Sortieren zu unterstützen

Wenn sich Ihre Produktlinie im Laufe der Zeit erweitert, können bestimmte Abfragen viele Ergebnisse liefern (Tausende, Zehntausende?), Die jedoch möglicherweise nur einen kleinen Teil Ihrer gesamten Produktlinie ausmachen. In diesen Fällen kann das Sortieren sogar ziemlich teuer sein, wenn es im Speicher durchgeführt wird, aber ein entsprechend gestalteter Index kann verwendet werden, um das Sortieren zu unterstützen.

Weitere Informationen finden Sie in der offiziellen PostgreSQL-Dokumentation, in der Indizes und ORDER BY beschrieben werden .

Wenn Sie einen Index erstellen, der Ihren ORDER BYAnforderungen entspricht

CREATE INDEX idx_product_sort ON products (score DESC, title);

Dann optimiert Postgres und entscheidet, ob die Verwendung des Index oder die Durchführung einer expliziten Sortierung kostengünstiger ist. Beachten Sie, dass es keine Garantie gibt dass Postgres den Index verwendet. Es wird versucht, die Leistung zu optimieren und zwischen der Verwendung des Index und der expliziten Sortierung zu wählen. Wenn Sie diesen Index erstellen, überwachen Sie ihn, um festzustellen, ob er ausreichend verwendet wird, um seine Erstellung zu rechtfertigen, und löschen Sie ihn, wenn die meisten Ihrer Sortierungen explizit durchgeführt werden.

Zu diesem Zeitpunkt wird Ihr "größter Knall fürs Geld" wahrscheinlich mehr verbrauchen work_mem, aber es gibt Fälle, in denen der Index das Sortieren unterstützen könnte.

Chris
quelle
Ich habe auch darüber nachgedacht, Junction Table zu verwenden, um GIN zu vermeiden. Sie haben jedoch nicht angegeben, wie dies beim Sortieren helfen soll. Ich denke, es wird nicht helfen. Ich habe versucht, die Produkttabelle mit einer Reihe von Produkt-IDs zu verbinden, die über eine GIN-Abfrage erfasst wurden. Ich glaube, dass dies der von Ihnen angebotenen Verknüpfung ziemlich ähnlich ist, und für diesen Vorgang konnte kein B-Tree-Index für Punktzahl und Titel verwendet werden. Vielleicht habe ich einen falschen Index erstellt. Könnten Sie das bitte näher erläutern?
Jaroslaw Stavnichiy
Entschuldigung, vielleicht habe ich es nicht klar genug erklärt. Die Änderung Ihrer work_memKonfiguration war als Lösung für Ihr Problem beim Sortieren auf der Festplatte sowie für Ihr Problem beim erneuten Überprüfen des Zustands gedacht. Wenn die Anzahl der Produkte zunimmt, benötigen Sie möglicherweise einen zusätzlichen Index zum Sortieren. Bitte beachten Sie meine Änderungen oben zur Verdeutlichung.
Chris