Ich benutze Postgres 9.4.
Das messages
hat das folgende Schema: messages gehört zu feed_id und hat posted_at, auch Nachrichten können eine übergeordnete Nachricht haben (bei Antworten).
Table "public.messages"
Column | Type | Modifiers
------------------------------+-----------------------------+-----------
message_id | character varying(255) | not null
feed_id | integer |
parent_id | character varying(255) |
posted_at | timestamp without time zone |
share_count | integer |
Indexes:
"messages_pkey" PRIMARY KEY, btree (message_id)
"index_messages_on_feed_id_posted_at" btree (feed_id, posted_at DESC NULLS LAST)
Ich möchte alle von bestellten Nachrichten zurückgeben share_count
, aber für jede parent_id
möchte ich nur eine Nachricht zurückgeben. Wenn also mehrere Nachrichten dieselbe haben parent_id
, wird nur die letzte ( posted_at
) zurückgegeben. Das parent_id
kann null sein, Nachrichten mit null parent_id
sollten alle zurückkehren.
Die Abfrage, die ich verwendet habe, ist:
WITH filtered_messages AS (SELECT *
FROM messages
WHERE feed_id IN (7)
AND (posted_at >= '2015-01-01 04:00:00.000000')
AND (posted_at < '2015-04-28 04:00:00.000000'))
SELECT *
FROM (SELECT DISTINCT ON(COALESCE(parent_id, message_id)) parent_id,
message_id,
posted_at,
share_count
FROM filtered_messages
ORDER BY COALESCE(parent_id, message_id), posted_at DESC NULLS LAST
) messages
ORDER BY share_count DESC NULLS LAST, posted_at DESC NULLS LAST;
Hier ist die http://sqlfiddle.com/#!15/588e5/1/0 , in der SQL Fiddle habe ich das Schema, die genaue Abfrage und das erwartete Ergebnis definiert.
Die Leistung der Abfrage ist jedoch langsam, sobald die Nachrichtentabelle groß wird. Ich habe versucht, mehrere Sortierindizes hinzuzufügen, aber der Index scheint nicht verwendet zu werden. Hier ist die Erklärung: http://explain.depesz.com/s/Sv2
Wie kann ich einen korrekten Index erstellen?
quelle
ORDER BY
in der Unterabfrage völlig nutzlos. Darüber hinaus kann der verknüpfte Plan nicht das Ergebnis der veröffentlichten Abfrage sein - beispielsweise wird dies nicht erwähntmetadata
.feed_id
und abposted_at
und Sie haben überhaupt nicht erwähntmetadata
, was ein JSON-Typ zu sein scheint? Bitte reparieren Sie Ihre Frage, um sie konsistent zu machen. Sie wählen im CTE> 500k Zeilen aus ... Wie viele Zeilen enthält die Tabelle? Wie viel Prozent der Zeilen wählen Sie normalerweise im CTE aus? Wie viel Prozent der Zeilen hatparent_id IS NULL
? Beachten Sie die Informationen im Tag [postgresql-performance] für Leistungsfragen.parent_id
? (min / avg / max)metadata
. share_count war tatsächlich im Laden . Derzeit enthält die Nachrichtentabelle 10 Mil-Daten, steigt jedoch schnell an. Ich denke, in Partitionstabellen für jede feed_id zu trennen. Da ich nur pro Feed-ID abrufe. Der Prozentsatz von parent_id null vs not null beträgt ungefähr 60% / 40%. Ein typischer Abruf liegt bei 1-2% der Tabelle. (ca. 100K Nachrichten) Die Leistung für 100K beträgt ca. 1s, aber sobald 500K + erreicht sind, wird der Bitmap-Index verwendet und normalerweise 10s.Antworten:
Abfrage
Diese Abfrage sollte auf jeden Fall wesentlich schneller sein:
Der CTE unternimmt hier nichts, was eine einfache Unterabfrage auch nicht liefern könnte. Und ein CTE führt eine Optimierungsbarriere ein, da er separat ausgeführt wird und sein Ergebnis materialisiert wird.
Sie haben eine Unterabfrageebene mehr, als Sie tatsächlich benötigen.
Der Ausdruck
(COALESCE(parent_id, message_id)
ist nicht mit einem einfachen Index kompatibel. Sie benötigen einen Index für diesen Ausdruck. Abhängig von der Datenverteilung ist dies jedoch möglicherweise auch nicht sehr nützlich. Folgen Sie meinen Links unten für detaillierte Informationen.Das Aufteilen des einfachen Falls
parent_id IS NULL
in einen separaten FallSELECT
kann das Optimum liefern oder auch nicht. Insbesondere nicht, wenn dies ohnehin ein seltener Fall ist. In diesem Fall kann eine kombinierte Abfrage mit einem Index(COALESCE(parent_id, message_id)
eine bessere Leistung erzielen. Andere Überlegungen gelten ...Indizes
Besonders wenn mit diesen Indizes unterstützt:
Die beiden Teilindizes decken die gesamte Tabelle zusammen ab und haben zusammen ungefähr die gleiche Größe wie ein einzelner Gesamtindex.
Die letzten beiden Spalten sind
parent_id, message_id
nur dann sinnvoll, wenn Sie nur Index-Scans erhalten . Andernfalls entfernen Sie sie aus beiden Indizes.SQL Fiddle.
Abhängig von fehlenden Details
DISTINCT ON
kann dies die beste Abfragetechnik für diesen Zweck sein oder auch nicht. Lesen Sie hier die ausführliche Erklärung:Und möglicherweise schnellere Alternativen hier:
quelle