Jak utworzyć indeks, aby przyspieszyć zagregowane zapytanie LIKE dla wyrażenia?

20

Mogę zadawać złe pytanie w tytule. Oto fakty:

Pracownicy działu obsługi klienta narzekają na długi czas reakcji podczas wyszukiwania klientów w interfejsie administracyjnym naszej witryny opartej na Django.

Używamy Postgres 8.4.6. Zacząłem rejestrować powolne zapytania i odkryłem tego winowajcę:

SELECT COUNT(*) FROM "auth_user" WHERE UPPER("auth_user"."email"::text) LIKE UPPER(E'%deyk%')

Uruchomienie tego zapytania zajmuje ponad 32 sekundy. Oto plan zapytań dostarczony przez EXPLAIN:

QUERY PLAN
Aggregate  (cost=205171.71..205171.72 rows=1 width=0)
  ->  Seq Scan on auth_user  (cost=0.00..205166.46 rows=2096 width=0)
        Filter: (upper((email)::text) ~~ '%DEYK%'::text)

Ponieważ jest to zapytanie wygenerowane przez Django ORM z zestawu Django QuerySet wygenerowanego przez aplikację administracyjną Django, nie mam żadnej kontroli nad samym zapytaniem. Indeks wydaje się logicznym rozwiązaniem. Próbowałem utworzyć indeks, aby to przyspieszyć, ale to nie miało znaczenia:

CREATE INDEX auth_user_email_upper ON auth_user USING btree (upper(email::text))

Co ja robię źle? Jak mogę przyspieszyć to zapytanie?

David Eyk
źródło

Odpowiedzi:

21

Nie ma obsługi indeksu dla LIKE/ ILIKEw PostgreSQL 8.4 - z wyjątkiem szukanych terminów zakotwiczonych w lewo .

Od wersji PostgreSQL 9.1 dodatkowy moduł pg_trgmudostępnia klasy operatorów dla indeksów trygramowych GIN i GiST obsługujących LIKE/ ILIKElub wyrażeń regularnych (operatorzy ~i przyjaciele). Zainstaluj raz na bazę danych:

CREATE EXTENSION pg_trgm;

Przykładowy indeks GIN:

CREATE INDEX tbl_col_gin_trgm_idx ON tbl USING gin (col gin_trgm_ops);

Związane z:

Erwin Brandstetter
źródło
2
To właściwie poprawna odpowiedź.
vonPetrushev
9

Ten indeks nie pomoże z powodu „%” na początku dopasowania - indeks BTREE może dopasować tylko prefiksy, a symbol wieloznaczny na początku zapytania oznacza, że ​​nie ma ustalonego prefiksu do wyszukania.

Dlatego wykonuje skanowanie tabeli i dopasowuje kolejno każdy rekord względem ciągu zapytania.

Prawdopodobnie musisz spojrzeć na użycie indeksu pełnotekstowego i operatorów dopasowywania tekstu zamiast na wyszukiwanie podciągów w LIKE, którym jesteś w tej chwili. Więcej informacji na temat wyszukiwania pełnotekstowego można znaleźć w dokumentacji:

http://www.postgresql.org/docs/8.4/static/textsearch-intro.html

W rzeczywistości zauważam z tej strony, że LIKE najwyraźniej nigdy nie używa indeksów, co wydaje mi się dziwne, ponieważ powinno być w stanie rozwiązać przedrostki z symbolami wieloznacznymi przy użyciu indeksu BTREE. Kilka szybkich testów sugeruje, że dokumentacja jest prawdopodobnie poprawna, w takim przypadku żadna ilość indeksowania nie pomoże, gdy używasz LIKE do rozwiązania zapytania.

TomH
źródło
Tego się bałem. Czy istnieje inny rodzaj indeksu, który pomoże? Jak powiedziałem, jestem trochę ograniczony w mojej zdolności wpływania na samo zapytanie.
David Eyk,
Prowadzenie %jest także niezbędną funkcją: przedstawiciele działu obsługi klienta potrzebują go do wyszukiwania kont klientów, zwłaszcza gdy w adresie e-mail jest literówka.
David Eyk,
Cóż, po kilku badaniach dotyczących LIKE i indeksowania pełnotekstowego, zaczynam rozumieć twój punkt widzenia.
David Eyk,
Na razie znalazłem sposób na stłumienie wiodącej symboli wieloznacznej. Okazuje się, że możesz użyć indeksu z LIKE, jeśli utworzysz indeks z odpowiednią klasą operatora . Dokumenty są tutaj: postgresql.org/docs/8.4/static/indexes-opclass.html
David Eyk
Sprawdź także, czy baza danych nie ma wzdęć. Jeśli masz dużo wzdęć w tej tabeli, zajmie to dużo czasu, aby zeskanować ją sekwencyjnie. Jeśli masz trochę przestojów, po prostu klastruj go na kluczu podstawowym i sprawdź, czy przyspieszy. Jeśli chcesz sprawdzić, czy nie ma wzdęć, możesz uruchomić analizę, a następnie uruchomić zapytanie tutaj: wiki.postgresql.org/wiki/Show_database_bloat . Aby uzyskać dokładniejsze wartości, zobacz dół tej strony.
Scott Marlowe,