W bazie danych Postgres 9.1 mam tabelę table1
z ~ 1,5 mln wierszy i kolumnęlabel
(uproszczone nazwy ze względu na to pytanie).
Istnieje funkcjonalny indeks trigram na lower(unaccent(label))
(unaccent()
został unieruchomiony, aby umożliwić jego użycie w indeksie).
Następujące zapytanie jest dość szybkie:
SELECT count(*) FROM table1
WHERE (lower(unaccent(label)) like lower(unaccent('%someword%')));
count
-------
1
(1 row)
Time: 394,295 ms
Ale następujące zapytanie jest wolniejsze:
SELECT count(*) FROM table1
WHERE (lower(unaccent(label)) like lower(unaccent('%someword and some more%')));
count
-------
1
(1 row)
Time: 1405,749 ms
Dodanie większej liczby słów jest jeszcze wolniejsze, mimo że wyszukiwanie jest bardziej rygorystyczne.
Próbowałem prostej sztuczki, aby uruchomić podkwerendę dla pierwszego słowa, a następnie kwerendy z pełnym ciągiem wyszukiwania, ale (niestety) planista zapytań przejrzał moje machinacje:
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%'));
Skan stosu bitmap w tabeli 1 (koszt = 16216.01..16220.04 wierszy = 1 szerokość = 212) (rzeczywisty czas = 1824.017..1824.019 wierszy = 1 pętli = 1) Ponownie sprawdź Cond: ((lower (unaccent ((label) :: text)) ~~ '% someord%' :: text) AND (lower (unaccent ((label) :: text)) ~~ '% nieco i trochę więcej %'::tekst)) -> Skanowanie indeksu bitmap na table1_label_hun_gin_trgm (koszt = 0,00..16216.01 wierszy = 1 szerokość = 0) (rzeczywisty czas = 1823.900..1823.900 wierszy = 1 pętli = 1) Index Cond: ((lower (unaccent ((label) :: text)) ~~ '% someord%' :: text) AND (lower (unaccent ((label) :: text)) ~~ '% nieco i trochę więcej %'::tekst)) Całkowity czas działania: 1824.064 ms
Moim największym problemem jest to, że ciąg wyszukiwania pochodzi z interfejsu internetowego, który może wysyłać dość długie ciągi, a zatem może być bardzo wolny i może również stanowić wektor DOS.
Więc moje pytania to:
- Jak przyspieszyć zapytanie?
- Czy istnieje sposób na podzielenie go na podkwerendy, aby było szybsze?
- Może późniejsza wersja Postgresa jest lepsza? (Próbowałem 9.4 i nie wydaje się to szybsze: wciąż ten sam efekt. Może późniejsza wersja?)
- Może potrzebna jest inna strategia indeksowania?
unaccent
niezmienność. Dodałem to do pytania.
unaccent
modułu. Jednym z powodów, dla których sugeruję opakowanie funkcji.
unaccent()
jest on również zapewniany przez dodatkowy moduł, a Postgres domyślnie nie obsługuje indeksów funkcji, ponieważ tak nie jestIMMUTABLE
. Musiałeś coś zmienić i powinieneś wspomnieć o tym, co zrobiłeś dokładnie w swoim pytaniu. Moja stała rada: stackoverflow.com/a/11007216/939860 . Ponadto indeksy trigram obsługują dopasowanie bez rozróżniania wielkości liter po wyjęciu z pudełka. Możesz uprościć:WHERE f_unaccent(label) ILIKE f_unaccent('%someword%')
- z pasującym indeksem. Szczegóły: stackoverflow.com/a/28636000/939860 .