Verwendung des Index zur Beschleunigung der Sortierung in Postgres


10

Ich benutze Postgres 9.4.

Das messageshat 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_idmö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_idkann null sein, Nachrichten mit null parent_idsollten 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?


Auf den ersten Blick ist das ORDER BYin 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ähnt metadata.
Dekso

Ihre Beschreibung deckt nicht die Rolle von feed_idund ab posted_atund Sie haben überhaupt nicht erwähnt metadata, 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 hat parent_id IS NULL? Beachten Sie die Informationen im Tag [postgresql-performance] für Leistungsfragen.
Erwin Brandstetter

Ebenfalls wichtig: Wie viele Zeilen für jede parent_id? (min / avg / max)
Erwin Brandstetter

Entschuldigung, ich habe versucht, die Frage klarer zu machen, indem ich einige der Spalten verkleinert habe 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.
Zhaohan Weng

Antworten:


9

Abfrage

Diese Abfrage sollte auf jeden Fall wesentlich schneller sein:

SELECT parent_id, message_id, posted_at, share_count
FROM   messages
WHERE  feed_id = 7
AND    posted_at >= '2015-01-01 4:0:0'
AND    posted_at <  '2015-04-28 4:0:0'
AND    parent_id IS NULL  -- match index condition
UNION ALL
(
SELECT DISTINCT ON(parent_id)
       parent_id, message_id, posted_at, share_count
FROM   messages
WHERE  feed_id = 7
AND    posted_at >= '2015-01-01 4:0:0'
AND    posted_at <  '2015-04-28 4:0:0'
AND    parent_id IS NOT NULL  -- match index condition
ORDER  BY parent_id, posted_at DESC NULLS LAST
)
ORDER  BY share_count DESC NULLS LAST, posted_at DESC NULLS LAST;
  • 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 NULLin einen separaten Fall SELECTkann 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:

CREATE INDEX messages_idx_null ON messages (
  feed_id
, posted_at DESC NULLS LAST
, share_count DESC NULLS LAST
, parent_id, message_id
)
WHERE parent_id IS NULL;

CREATE INDEX messages_idx_notnull ON messages (
  feed_id
, posted_at DESC NULLS LAST
, share_count DESC NULLS LAST
, parent_id, message_id
)
WHERE parent_id IS NOT NULL;

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_idnur dann sinnvoll, wenn Sie nur Index-Scans erhalten . Andernfalls entfernen Sie sie aus beiden Indizes.

SQL Fiddle.

Abhängig von fehlenden Details DISTINCT ONkann 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:

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.