Изменения производительности запросов 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);

Точно так же вы можете изменить верхнюю функцию на любую другую функцию, которую вы хотите.