Как создать индекс, чтобы ускорить совокупный запрос LIKE по выражению?

20

Возможно, я задаю не тот вопрос в названии. Вот факты:

Мои специалисты по обслуживанию клиентов жалуются на медленное время отклика при поиске клиентов в интерфейсе администрирования нашего сайта на Django.

Мы используем Postgres 8.4.6. Я начал регистрировать медленные запросы и обнаружил этого преступника:

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

Этот запрос занимает более 32 секунд для выполнения. Вот план запроса, предоставленный 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)

Поскольку это запрос, сгенерированный Django ORM из Django QuerySet, сгенерированный приложением Django Admin, я не имею никакого контроля над самим запросом. Индекс кажется логичным решением. Я попытался создать индекс, чтобы ускорить это, но это не имело значения:

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

Что я делаю неправильно? Как я могу ускорить этот запрос?

Дэвид Эйк
источник

Ответы:

21

Там нет поддержки индекса LIKE/ ILIKEв PostgreSQL 8.4 - за исключением оставшихся якорных условий поиска .

Начиная с PostgreSQL 9.1 дополнительный модуль pg_trgmпредоставляет классы операторов для индексов триграмм GIN и GiST, поддерживающие LIKE/ ILIKEили регулярные выражения (операторы ~и друзья). Установить один раз для базы данных:

CREATE EXTENSION pg_trgm;

Пример индекса GIN:

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

Связанный:

Эрвин Брандштеттер
источник
2
Это на самом деле правильный ответ.
фон Петрушев
9

Этот индекс не поможет, потому что в начале вашего совпадения есть «%» - индекс BTREE может соответствовать только префиксам, а подстановочный знак в начале вашего запроса означает, что нет фиксированного префикса для поиска.

Вот почему он выполняет сканирование таблицы и сопоставляет каждую запись по очереди со строкой запроса.

Вам, вероятно, нужно взглянуть на использование полнотекстового индекса и операторов сопоставления текста, а не выполнять поиск по подстроке с LIKE, которым вы являетесь в данный момент. Вы можете найти больше о полнотекстовом поиске в документации:

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

На самом деле, на этой странице я заметил, что LIKE, по-видимому, никогда не использует индексы, что мне кажется странным, поскольку он должен иметь возможность разрешать префиксы без подстановочных знаков, используя индекс BTREE. Несколько быстрых тестов показывают, что документация, вероятно, верна, но в этом случае никакое количество индексации не поможет, пока вы используете LIKE для разрешения запроса.

TomH
источник
Вот чего я боялся. Есть ли другой вид индекса, который поможет? Как я уже сказал, я немного ограничен в своей способности влиять на сам запрос.
Дэвид Эйк
Кроме того, ведущая %роль является необходимой функцией: представителям службы поддержки клиентов она нужна для поиска учетных записей клиентов, особенно если в адресе электронной почты есть опечатка.
Дэвид Эйк
Ну, после небольшого исследования LIKE и полнотекстовой индексации, и я начинаю понимать вашу точку зрения.
Дэвид Эйк
На данный момент я нашел способ подавить главный подстановочный знак. Оказывается, вы можете использовать индекс с LIKE, если вы создаете индекс с соответствующим классом операторов . Документы находятся здесь: postgresql.org/docs/8.4/static/indexes-opclass.html
Дэвид Эйк,
Кроме того, проверьте свою базу данных на раздувание. Если в этой таблице много раздувания, потребуется много времени, чтобы его отсканировать. Если у вас есть время простоя, просто сгруппируйте его по первичному ключу и посмотрите, станет ли он быстрее. Если вы хотите проверить на раздувание, вы можете запустить анализ, а затем выполнить запрос здесь: wiki.postgresql.org/wiki/Show_database_bloat . Для более точных значений, смотрите в нижней части этой страницы.
Скотт Марлоу