Изменения производительности запросов PostgreSQL LIKE
Я видел довольно большое изменение в времени отклика относительно запросов LIKE
к определенной таблице в моей базе данных. Иногда я получаю результаты в течение 200-400 мс (очень приемлемо), но в других случаях для возврата результатов может потребоваться до 30 секунд.
Я понимаю, что запросы LIKE
очень ресурсоемкие, но я просто не понимаю, почему в ответах будет такая большая разница. Я построил индекс btree в поле owner1
, но я не думаю, что он помогает с запросами LIKE
. У кого-нибудь есть идеи?
Пример SQL:
SELECT gid, owner1 FORM parcels
WHERE owner1 ILIKE '%someones name%' LIMIT 10
Я также пробовал:
SELECT gid, owner1 FROM parcels
WHERE lower(owner1) LIKE lower('%someones name%') LIMIT 10
и
SELECT gid, owner1 FROM parcels
WHERE lower(owner1) LIKE lower('someones name%') LIMIT 10
Аналогичные результаты.
Количество строк таблицы: около 95 000.
Ответы
Ответ 1
FTS не поддерживает LIKE
ранее принятый ответ был неверным. Полнотекстовый поиск с полнотекстовыми индексами вовсе не для оператора LIKE
, он имеет свои собственные операторы и не работает для произвольных строк. Он работает на словах на основе словарей и stemming. Он поддерживает сопоставление префиксов для слов, но не с оператором LIKE
:
Индексы триграмм для LIKE
Установите дополнительный модуль pg_trgm
, который предоставляет классы операторов для индексов триграмм GIN и GiST для поддержки всех шаблонов LIKE
и ILIKE
, а не только слева. из них:
Пример индекса:
CREATE INDEX tbl_col_gin_trgm_idx ON tbl USING gin (col gin_trgm_ops);
Или:
CREATE INDEX tbl_col_gist_trgm_idx ON tbl USING gist (col gist_trgm_ops);
Пример запроса:
SELECT * FROM tbl WHERE col LIKE '%foo%'; -- leading wildcard
SELECT * FROM tbl WHERE col ILIKE '%foo%'; -- works case insensitively as well
триграмм? Как насчет более коротких строк?
Слова с менее чем 3 буквами в индексированных значениях по-прежнему работают. Руководство:
Каждое слово считается с двумя пробелами и одним пробелом суффикс при определении набора триграмм, содержащихся в строке.
И шаблоны поиска с менее чем 3 буквы? Руководство:
И для поиска LIKE
, и для регулярных выражений имейте в виду, что шаблон без извлекаемых триграмм выродится в сканирование с полным индексом.
Это означает, что сканирование индекса/растрового индекса все еще работает (планы запросов для подготовленного оператора не будут нарушены), это просто не принесет вам большей производительности. Как правило, это не большая потеря, поскольку 1- или двухбуквенные строки едва ли избирательны (более нескольких процентов совпадают с базовой таблицей), и поддержка индекса не повысила бы производительность с самого начала, потому что полное сканирование таблицы происходит быстрее.
text_pattern_ops
для сопоставления префиксов
Для шаблонов с левой привязкой (без начального подстановочного знака) вы получаете оптимум с подходящим оператором класса для индекса btree: text_pattern_ops
или varchar_pattern_ops
. Обе встроенные функции стандартного Postgres, дополнительный модуль не требуется. Аналогичная производительность, но гораздо меньший показатель.
Пример индекса:
CREATE INDEX tbl_col_text_pattern_ops_idx ON tbl(col text_pattern_ops);
Пример запроса:
SELECT * FROM tbl WHERE col LIKE 'foo%'; -- no leading wildcard
Или, если вы должны работать с базой данных с локалью 'C' (фактически без локали), тогда все будет отсортировано в соответствии с порядком байтов в любом случае, и обычный индекс btree с классом операторов по умолчанию сделает свою работу.
Больше подробностей, объяснений, примеров и ссылок в этих связанных ответах на dba.SE:
Ответ 2
Возможно, быстрые - это привязанные шаблоны с чувствительностью к регистру, которые могут использовать индексы. то есть нет дикой карты в начале строки соответствия, чтобы исполнитель мог использовать сканирование диапазона индекса. (соответствующий комментарий в документах находится здесь). Более низкие и похожие также потеряют способность использовать индекс, если вы специально не создаете индекс для этой цели (см. функциональные индексы).
Если вы хотите найти строку в середине поля, вы должны изучить полный текст или индексы триграмм. Первый из них находится в ядре Postgres, другой доступен в модулях Contrib.
Ответ 3
Вы можете установить Wildspeed, другой тип индекса в PostgreSQL. Wildspeed работает с групповыми символами% word%, без проблем. Недостатком является размер индекса, это может быть большим, очень большим.
Ответ 4
Пожалуйста, выполните нижеуказанный запрос для улучшения производительности запроса LIKE в postgresql.
создайте такой индекс для больших таблиц:
CREATE INDEX <indexname> ON <tablename> USING btree (<fieldname> text_pattern_ops)
Ответ 5
Недавно у меня была похожая проблема с таблицей, содержащей 200000 записей, и мне нужно делать повторные запросы LIKE. В моем случае искомая строка была исправлена. Другие поля менялись. Потому что я смог переписать:
SELECT owner1 FROM parcels
WHERE lower(owner1) LIKE lower('%someones name%');
в
CREATE INDEX ix_parcels ON parcels(position(lower('someones name') in lower(owner1)));
SELECT owner1 FROM parcels
WHERE position(lower('someones name') in lower(owner1)) > 0;
Я был рад, когда запросы быстро возвращались, и проверил, что индекс используется с EXPLAIN ANALYZE
:
Bitmap Heap Scan on parcels (cost=7.66..25.59 rows=453 width=32) (actual time=0.006..0.006 rows=0 loops=1)
Recheck Cond: ("position"(lower(owner1), 'someones name'::text) > 0)
-> Bitmap Index Scan on ix_parcels (cost=0.00..7.55 rows=453 width=0) (actual time=0.004..0.004 rows=0 loops=1)
Index Cond: ("position"(lower(owner1), 'someones name'::text) > 0)
Planning time: 0.075 ms
Execution time: 0.025 ms
Ответ 6
Ваши похожие запросы, вероятно, не могут использовать созданные вами индексы, потому что:
1) ваши критерии LIKE начинаются с подстановочного знака.
2) вы использовали функцию со своими критериями LIKE.
Ответ 7
во что бы то ни стало, Django ORM стремится использовать UPPER(text)
для всех запросов LIKE
, чтобы сделать его нечувствительным к регистру,
Добавление индекса в UPPER(column::text)
значительно ускорило мою систему, в отличие от любой другой вещи.
Что касается ведущих%, да, что не будет использовать индекс. Смотрите этот блог для отличного объяснения:
https://use-the-index-luke.com/sql/where-clause/searching-for-ranges/like-performance-tuning
Ответ 8
Когда вы используете предложение для столбца с функциями, например, LIKE, ILIKE, верхний, нижний и т.д. Тогда postgres не будет учитывать ваш обычный индекс. Он выполнит полное сканирование таблицы, проходящей через каждую строку, и поэтому будет медленным.
Правильный способ - создать новый индекс по вашему запросу. Например, если я хочу сопоставить столбец без учета регистра, а мой столбец - varchar. Тогда вы можете сделать это следующим образом.
create index ix_tblname_col_upper on tblname (UPPER(col) varchar_pattern_ops);
Точно так же, если ваш столбец является текстом, вы делаете что-то вроде этого
create index ix_tblname_col_upper on tblname (UPPER(col) text_pattern_ops);
Точно так же вы можете изменить верхнюю функцию на любую другую функцию, которую вы хотите.