In einer Postgres 9.1-Datenbank habe ich eine Tabelle table1
mit ~ 1,5 Millionen Zeilen und einer Spalte label
(vereinfachte Namen für diese Frage).
Es gibt einen funktionalen Trigramm-Index für lower(unaccent(label))
( unaccent()
wurde unveränderlich gemacht, damit er im Index verwendet werden kann).
Die folgende Abfrage ist ziemlich schnell:
SELECT count(*) FROM table1
WHERE (lower(unaccent(label)) like lower(unaccent('%someword%')));
count
-------
1
(1 row)
Time: 394,295 ms
Die folgende Abfrage ist jedoch langsamer:
SELECT count(*) FROM table1
WHERE (lower(unaccent(label)) like lower(unaccent('%someword and some more%')));
count
-------
1
(1 row)
Time: 1405,749 ms
Das Hinzufügen weiterer Wörter ist sogar noch langsamer, obwohl die Suche strenger ist.
Ich habe einen einfachen Trick ausprobiert, um eine Unterabfrage für das erste Wort und dann eine Abfrage mit der vollständigen Suchzeichenfolge auszuführen, aber (leider) hat der Abfrageplaner meine Aktionen durchgesehen:
EXPLAIN ANALYZE
SELECT * FROM (
SELECT id, title, label from table1
WHERE lower(unaccent(label)) like lower(unaccent('%someword%'))
) t1
WHERE lower(unaccent(label)) like lower(unaccent('%someword and some more%'));
Bitmap-Heap-Scan für Tabelle 1 (Kosten = 16216.01..16220.04 Zeilen = 1 Breite = 212) (tatsächliche Zeit = 1824.017..1824.019 Zeilen = 1 Schleifen = 1) Überprüfen Sie erneut Cond: ((lower (unaccent ((label) :: text)) ~~ '% someword%' :: text) UND (lower (unaccent ((label) :: text)) ~~ '% someword und einige mehr %'::Text)) -> Bitmap-Index-Scan für table1_label_hun_gin_trgm (Kosten = 0,00..16216,01 Zeilen = 1 Breite = 0) (tatsächliche Zeit = 1823.900..1823.900 Zeilen = 1 Schleifen = 1) Indexbedingung: ((niedriger (unaccent ((label) :: text)) ~~ '% someword%' :: text) UND (niedriger (unaccent ((label) :: text)) ~~ '% someword und einige mehr %'::Text)) Gesamtlaufzeit: 1824.064 ms
Mein letztendliches Problem ist, dass die Suchzeichenfolge von einer Webschnittstelle stammt, die möglicherweise sehr lange Zeichenfolgen sendet und daher sehr langsam ist und möglicherweise auch einen DOS-Vektor darstellt.
Meine Fragen sind also:
- Wie kann die Abfrage beschleunigt werden?
- Gibt es eine Möglichkeit, es in Unterabfragen aufzuteilen, damit es schneller ist?
- Vielleicht ist eine spätere Version von Postgres besser? (Ich habe 9.4 ausprobiert und es scheint nicht schneller: immer noch der gleiche Effekt. Vielleicht eine spätere Version?)
- Möglicherweise ist eine andere Indizierungsstrategie erforderlich?
unaccent
unveränderlich. Ich habe dies der Frage hinzugefügt.
unaccent
Moduls überschrieben wird. Einer der Gründe, warum ich stattdessen einen Funktionswrapper vorschlage.
unaccent()
auch durch ein zusätzliches Modul bereitgestellt wird und Postgres standardmäßig keine Indizes für die Funktion unterstützt, da dies nicht der Fall istIMMUTABLE
. Sie müssen etwas geändert haben und Sie sollten in Ihrer Frage genau angeben, was Sie getan haben. Mein ständiger Rat: stackoverflow.com/a/11007216/939860 . Darüber hinaus unterstützen Trigrammindizes den Abgleich ohne Berücksichtigung der Groß- und Kleinschreibung. Sie können vereinfachen:WHERE f_unaccent(label) ILIKE f_unaccent('%someword%')
- mit einem passenden Index. Details: stackoverflow.com/a/28636000/939860 .