Diese Frage speziell für Postgres zu stellen, da es eine gute Unterstützung für R-Tree / Spatial-Indizes bietet.
Wir haben die folgende Tabelle mit einer Baumstruktur (Nested Set-Modell) von Wörtern und ihren Häufigkeiten:
lexikon
-------
_id integer PRIMARY KEY
word text
frequency integer
lset integer UNIQUE KEY
rset integer UNIQUE KEY
Und die Abfrage:
SELECT word
FROM lexikon
WHERE lset BETWEEN @Low AND @High
ORDER BY frequency DESC
LIMIT @N
Ich nehme an, dass ein Deckungsindex (lset, frequency, word)
für nützlich wäre, aber ich bin der Meinung, dass er möglicherweise keine gute Leistung erbringt, wenn zu viele lset
Werte im (@High, @Low)
Bereich liegen.
(frequency DESC)
Manchmal kann auch ein einfacher Index für ausreichend sein, wenn eine Suche mit diesem Index zu einem frühen Zeitpunkt die @N
Zeilen ergibt, die der Bereichsbedingung entsprechen.
Es scheint jedoch, dass die Leistung stark von den Parameterwerten abhängt.
Gibt es eine Möglichkeit, die Leistung zu steigern, unabhängig davon, ob der Bereich (@Low, @High)
groß oder klein ist und ob die Wörter mit der höchsten Frequenz zum Glück im (engen) ausgewählten Bereich liegen?
Würde ein R-Baum / räumlicher Index helfen?
Das Hinzufügen von Indizes, das Umschreiben der Abfrage und das Neuentwerfen der Tabelle unterliegen keinen Einschränkungen.
quelle
lset,rset
undword
.Antworten:
Möglicherweise können Sie eine bessere Leistung erzielen, indem Sie zuerst in Zeilen mit höheren Frequenzen suchen. Dies kann erreicht werden, indem die Frequenzen "granuliert" und dann prozedural durchlaufen werden, zum Beispiel wie folgt:
--Prüf- und
lexikon
Dummy-Daten:granule
Analyse (hauptsächlich zur Information und Abstimmung):Funktion zum Scannen hoher Frequenzen zuerst:
Ergebnisse (Timings sollten wahrscheinlich mit einer Prise Salz genommen werden, aber jede Abfrage wird zweimal ausgeführt, um jeglichem Caching entgegenzuwirken.)
Verwenden Sie zuerst die Funktion, die wir geschrieben haben:
und dann mit einem einfachen Index-Scan:
Abhängig von Ihren realen Daten möchten Sie wahrscheinlich die Anzahl der Granulate und die Funktion zum Einfügen von Zeilen in diese variieren. Die tatsächliche Häufigkeitsverteilung ist hier ebenso entscheidend wie die erwarteten Werte für die
limit
Klausel und die Größe derlset
gesuchten Bereiche.quelle
width_granule=8
zwischengranulae_start
undgranulae_end
von der vorherigen Ebene?frequency
wie eine große Lücke zwischen 1e6 / 2 und 1e6 / 3 erzeugt wird: Je höher die Zeilennummer wird, desto kleiner ist die Lücke. Trotzdem, danke für diesen tollen Ansatz !!Installieren
Ich baue auf @ Jacks Setup auf, um es den Leuten einfacher zu machen, zu folgen und zu vergleichen. Getestet mit PostgreSQL 9.1.4 .
Ab hier gehe ich einen anderen Weg:
Hilfstisch
Diese Lösung fügt der Originaltabelle keine Spalten hinzu, sondern benötigt lediglich eine winzige Hilfstabelle. Ich habe es in das Schema eingefügt
public
und ein beliebiges Schema Ihrer Wahl verwendet.Tabelle sieht so aus:
Da die Spalte
cond
später in dynamischem SQL verwendet werden soll, müssen Sie diese Tabelle sicher machen . Führen Sie immer eine Schemaqualifizierung der Tabelle durch, wenn Sie sich nicht sicher sind, ob ein entsprechender Stand vorliegtsearch_path
, und widerrufen Sie Schreibberechtigungen fürpublic
(und jede andere nicht vertrauenswürdige Rolle):Die Tabelle
lex_freq
dient drei Zwecken:Indizes
Diese
DO
Anweisung erstellt alle benötigten Indizes:Alle diese Teilindizes spannen zusammen , um die Tabelle einmal. Sie sind ungefähr so groß wie ein Basisindex für die gesamte Tabelle:
Bisher nur 21 MB Indizes für eine Tabelle mit 50 MB.
Ich erstelle die meisten Teilindizes auf
(lset, frequency DESC)
. Die zweite Spalte hilft nur in Sonderfällen. Da beide beteiligten Spalten vom Typ sindinteger
, wird der Index aufgrund der Besonderheiten der Datenausrichtung in Kombination mit MAXALIGN in PostgreSQL durch die zweite Spalte nicht größer. Es ist ein kleiner Gewinn für kaum Kosten.Dies hat keinen Sinn für Teilindizes, die nur eine Frequenz umfassen. Die sind gerade an
(lset)
. Erstellte Indizes sehen so aus:Funktion
Die Funktion ähnelt in etwa der @ Jack-Lösung:
Hauptunterschiede:
dynamisches SQL mit
RETURN QUERY EXECUTE
.Während wir die Schritte durchlaufen, kann ein anderer Abfrageplan nützlich sein. Der Abfrageplan für statisches SQL wird einmal generiert und dann wiederverwendet. Dies spart möglicherweise zusätzlichen Aufwand. In diesem Fall ist die Abfrage jedoch einfach und die Werte sind sehr unterschiedlich. Dynamic SQL wird ein großer Gewinn sein.
Dynamisch
LIMIT
für jeden Abfrageschritt.Dies hat mehrere Vorteile: Erstens werden Zeilen nur bei Bedarf abgerufen. In Kombination mit dynamischem SQL können dadurch auch verschiedene Abfragepläne erstellt werden. Zweitens: Es ist kein zusätzlicher
LIMIT
Funktionsaufruf erforderlich , um den Überschuss zu kürzen.Benchmark
Installieren
Ich habe vier Beispiele ausgewählt und mit jedem drei verschiedene Tests durchgeführt. Ich habe das Beste aus fünf genommen, um es mit dem warmen Cache zu vergleichen:
Die rohe SQL-Abfrage des Formulars:
Dasselbe nach dem Erstellen dieses Index
Benötigt ungefähr den gleichen Platz wie alle meine Teilindizes zusammen:
Die Funktion
Ergebnisse
1: Gesamtlaufzeit: 315,458 ms
2: Gesamtlaufzeit: 36,458 ms
3: Gesamtlaufzeit: 0,330 ms
1: Gesamtlaufzeit: 294.819 ms
2: Gesamtlaufzeit: 18.915 ms
3: Gesamtlaufzeit: 1.414 ms
1: Gesamtlaufzeit: 426.831 ms
2: Gesamtlaufzeit: 217.874 ms
3: Gesamtlaufzeit: 1.611 ms
1: Gesamtlaufzeit: 2458.205 ms
2: Gesamtlaufzeit: 2458.205 ms - Für große Bereiche von lset ist der seq-Scan schneller als der Index.
3: Gesamtlaufzeit: 0,266 ms
Fazit
Wie erwartet wächst der Nutzen der Funktion mit größeren
lset
und kleineren ReichweitenLIMIT
.Mit sehr kleinen Reichweiten von
lset
ist die unformatierte Abfrage in Kombination mit dem Index tatsächlich schneller . Sie wollen testen und vielleicht verzweigen: rohe Abfrage für kleine Bereichelset
, sonst Funktionsaufruf. Das könnte man sogar einfach in die Funktion für ein "Beste aus beiden Welten" einbauen - das würde ich tun.Je nach Datenverteilung und typischen Abfragen werden weitere Schritte ausgeführt
lex_freq
zur Leistungsverbesserung beitragen. Testen Sie, um den Sweet Spot zu finden. Mit den hier vorgestellten Tools sollte es einfach zu testen sein.quelle
Ich sehe keinen Grund, die Wortspalte in den Index aufzunehmen. Also dieser Index
Ihre Anfrage wird schnell ausgeführt.
UPD
Derzeit gibt es in PostgreSQL keine Möglichkeiten, einen Deckungsindex zu erstellen. Es gab eine Diskussion über diese Funktion in der PostgreSQL-Mailingliste http://archives.postgresql.org/pgsql-performance/2012-06/msg00114.php
quelle
Verwenden eines GIST-Index
Es kommt darauf an, was Sie mit Fasten meinen: Sie müssen offensichtlich jede Zeile im Bereich besuchen, weil Ihre Abfrage lautet
ORDER freq DESC
. Schüchtern deckt der Abfrageplaner dies bereits ab, wenn ich die Frage verstehe,Hier erstellen wir eine Tabelle mit 10k Zeilen
(5::int,random()::double precision)
Wir indizieren es,
Wir fragen es ab,
Wir bekommen eine
Seq Scan on t
. Dies liegt einfach daran, dass unsere Selektivitätsschätzungen pg zu dem Schluss kommen lassen, dass der Heap-Zugriff schneller ist als das Scannen eines Index und das erneute Überprüfen. Wir machen es saftiger, indem wir 1.000.000 weitere Zeilen(42::int,random()::double precision)
einfügen, die nicht zu unserem "Sortiment" passen.Und dann fordern wir,
Sie können hier sehen, dass wir in 4.6 MS einen Index-Only-Scan durchführen .
Wenn Sie den Bereich auf die gesamte Tabelle ausweiten, wird logischerweise ein weiterer seq-Scan erstellt, und wenn Sie ihn mit einer weiteren Milliarde Zeilen ausweiten, wird ein weiterer Index-Scan erstellt.
Also zusammenfassend
quelle