Hier ist die Abfrage:
SELECT "products".*
FROM "products"
WHERE (status > 100)
AND "products"."above_revenue_average" = 't'
AND ("products"."category_id" NOT IN (5))
ORDER BY "products"."start_date" DESC
Ich habe einen Index für status
und start_date
.
Jedes Mal, wenn ich die Abfrage in meiner Anwendung ausführe, wird in den Protokollen Folgendes angezeigt:
[WHITE] temporary file:
path "pg_tblspc/16386/PG_9.3_201306121/pgsql_tmp/pgsql_tmp2544.0", size 37093376
Query: SELECT "products".* FROM "products" WHERE (status > 100)
AND "products"."above_revenue_average" = 't'
AND ("products"."category_id" NOT IN (5)) ORDER BY "products"."start_date" DESC
Ich glaube, diese temporäre Dateierstellung ist die Ursache für die langsame Leistung.
Beim Ausführen eines EXPLAIN ANALYZE
bekomme ich folgende Ergebnisse:
QUERY PLAN
Sort (cost=63395.28..63403.51 rows=16460 width=524)
(actual time=524.134..562.635 rows=65294 loops=1)
Sort Key: start_date
Sort Method: external merge Disk: 36224kB
-> Bitmap Heap Scan on products
(cost=4803.40..60389.73 rows=16460 width=524)
(actual time=27.390..397.879 rows=65294 loops=1)
Recheck Cond: (status > 100)
Filter: (above_revenue_average AND (category_id <> 5))
Rows Removed by Filter: 25115
-> Bitmap Index Scan on index_products_on_status
(cost=0.00..4802.58 rows=89662 width=0)
(actual time=18.006..18.006 rows=90411 loops=1)
Index Cond: (status > 100)
Total runtime: 577.870 ms
(10 rows)
Ich habe dann http://explain.depesz.com/ verwendet , um es ein bisschen lesbarer zu machen:
Statistiken pro Knotentyp
+-------------------+-------+--------------+------------+
| node type | count | sum of times | % of query |
+-------------------+-------+--------------+------------+
| Bitmap Heap Scan | 1 | 379.873 ms | 67.5 % |
| Bitmap Index Scan | 1 | 18.006 ms | 3.2 % |
| Sort | 1 | 164.756 ms | 29.3 % |
+-------------------+-------+--------------+------------+
Pro Tabelle Statistiken
+------------------+------------+--------------+------------+
| Table name | Scan count | Total time | % of query |
+------------------+------------+--------------+------------+
| scan type | count | sum of times | % of table |
| products | 1 | 379.873 ms | 67.5 % |
| Bitmap Heap Scan | 1 | 379.873 ms | 100.0 % |
+------------------+------------+--------------+------------+
Kann ich die Datenbankleistung durch Hinzufügen weiterer Indizes erhöhen? Vielleicht einige zusammengesetzte? Irgendwelche Ideen?
EXPLAIN (BUFFERS, ANALYZE)
Erzeugt weitere für den Fall relevante Informationen (wie in den Tag-Informationen für [postgresql-performance] angegeben . Bitte geben Sie immer Ihre Version von Postgres an. Außerdem sagt uns eine einzelne Abfrage nicht, welche Ihrer Prädikate (WHERE
Bedingungen) unveränderlich sind und welche variieren (Auf welche Weise?) Wir können nicht ernsthaft raten, wie Indizes ohne weitere Informationen optimiert werden können.Antworten:
work_mem
Offensichtlich wird der Sortiervorgang auf die Festplatte übertragen:
Weitere
work_mem
können bei der Abfrage helfen, wie bereits von @Kassandry vorgeschlagen . Erhöhen Sie die Einstellung, bis SieMemory
stattDisk
in derEXPLAIN
Ausgabe sehen. Aber es ist wahrscheinlich eine schlechte Idee, die allgemeine Einstellung basierend auf einer Abfrage zu erhöhen. Die richtige Einstellung hängt vom verfügbaren RAM und Ihrer vollständigen Situation ab. Beginnen Sie mit dem Lesen im Postgres-Wiki .Um Ihre Abfrage nur zu korrigieren, stellen Sie
work_mem
hoch genug für Ihre Transaktion nur mitSET LOCAL
in derselben Transaktion ein .Sie benötigen wahrscheinlich etwas mehr als 40 MB. Die In-Memory-Darstellung ist etwas größer als die Darstellung auf der Festplatte. Verbunden:
Abfrage
Ihre Anfrage (nach dem Trimmen von Rauschen):
Ihre Reihen sind ein halbes Kilobyte breit (
width=524
). Müssen Sie alle Spalten zurückgeben? (In der Regel nicht.) Listen Sie nur Spalten in derSELECT
Liste auf, die Sie aus Ihrer Abfrage benötigen, um die Gesamtleistung zu verbessern, insbesondere da Sie bereitswork_mem
Probleme haben .Kann eine der beteiligten Spalten NULL sein? Besonders wichtig für
category_id
undstart_date
. Vielleicht möchten Sie sich in diesem Fall anpassen ...Index
Ein mehrspaltiger Index kann sicherlich die Leistung verbessern. ( Wie @Paul umrissen ). Sie müssen Kosten und Gewinn abwägen. Wenn die Leistung für diese Abfrage wichtig oder sehr häufig ist, wählen Sie sie aus. Erstellen Sie nicht für jede Abfrage einen speziellen Index. So wenige wie möglich, so viele wie nötig. Indizes sind leistungsfähiger, wenn sie gemeinsam genutzt werden. Dies erhöht die Wahrscheinlichkeit, dass mehr von ihnen im Cache verbleiben.
Eine
boolean
Spalte wieabove_revenue_average
ist ein typischer Kandidat für eine Bedingung in einem Teilindex und nicht für eine Indexspalte.Meine wilde Vermutung basiert auf unvollständigen Informationen:
Verwenden Sie
DESC NULLS LAST
in Index- und Abfrage wennstart_date
NULL sein kann.quelle
Abhängig davon, wie selektiv die kombinierten Prädikate sind, würde ich mir einen guten Index für diese bestimmte Abfrage vorstellen:
Dies
SELECT *
ist möglicherweise problematisch, da der obige Index nicht alle Spalten enthält. Wenn der Index dadurch nicht vom Planer ausgewählt wird, können Sie zuerst nur die Primärschlüsselspalten abrufen und dann zur Tabelle zurückkehren, um die zusätzlichen Spalten nur für die ausgewählten Schlüssel zu sammeln.quelle
Eine der einfachsten und effektivsten Möglichkeiten, die Leistung bei dieser Art von Abfrage zu steigern, besteht darin, die Abfrage auszuführen
SET work_mem=40MB
(da Sie ~ 32 MB temporäre Datei zum Sortieren haben und ein wenig mehr hilft) und dann abzufragen, ob sich derEXPLAIN ANALYZE
Plan ändert Festplatte auf eine In-Memory-Sortierung. Führen Sie anschließend ausRESET work_mem
, um den Wert auf den Standardwert in Ihrem zurückzusetzenpostgresql.conf
.Die Grundidee wird unter work_mem unter folgendem Link behandelt: Optimieren Ihres PostgreSQL-Servers . Das Ändern dieser Einstellung für eine einzelne Abfrage sollte Ihnen die Vorteile bieten und die genannten Nachteile vermeiden.
Ich hoffe, das hilft. =)
quelle
SET work_mem=40MB
den Anfang der Transaktion in Ihre Anwendung einfügen, gilt diese für jeden Lauf dieser bestimmten Abfrage, unabhängig davon, ob Heroku sie am Ende zurücksetzt oder nicht. Ich bin mir nicht sicher, ob es eine Konfigurationsoption für Heroku gibt, mit der Siework_mem
leider für Ihre gesamte Konfiguration festlegen können .