В базе данных Postgres 9.1 у меня есть таблица table1
с ~ 1,5M строк и столбцом label
(упрощенные имена ради этого вопроса).
Имеется функциональный индекс-триграмм lower(unaccent(label))
( unaccent()
сделан неизменным, чтобы его можно было использовать в индексе).
Следующий запрос довольно быстрый:
SELECT count(*) FROM table1
WHERE (lower(unaccent(label)) like lower(unaccent('%someword%')));
count
-------
1
(1 row)
Time: 394,295 ms
Но следующий запрос медленнее:
SELECT count(*) FROM table1
WHERE (lower(unaccent(label)) like lower(unaccent('%someword and some more%')));
count
-------
1
(1 row)
Time: 1405,749 ms
И добавление большего количества слов еще медленнее, хотя поиск более строгий.
Я попробовал простой трюк для запуска подзапроса для первого слова, а затем для запроса с полной строкой поиска, но (к сожалению) планировщик запросов проследил мои махинации:
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%'));
Сканирование кучи растровых изображений в таблице 1 (стоимость = 16216.01..16220.04 строк = 1 ширина = 212) (фактическое время = 1824.017..1824.019 строк = 1 цикл = 1) Перепроверьте Cond: ((lower (unaccent ((label) :: text)) ~~ '% someword%' :: text) AND (Lower (unaccent ((label) :: text))) ~~ '% someword и некоторые другие %'::текст)) -> Сканирование индекса растрового изображения для table1_label_hun_gin_trgm (стоимость = 0.00..16216.01 строк = 1 ширина = 0) (фактическое время = 1823.900..1823.900 строк = 1 цикл = 1) Индекс Cond: ((lower (unaccent ((label) :: text)) ~~ '% someword%' :: text) AND (lower (unaccent ((label) :: text)) ~~ '% someword и некоторые другие %'::текст)) Общее время выполнения: 1824,064 мс
Моя конечная проблема заключается в том, что строка поиска поступает из веб-интерфейса, который может отправлять довольно длинные строки и, следовательно, быть довольно медленным и также может представлять собой вектор DOS.
Итак, мои вопросы:
- Как ускорить запрос?
- Есть ли способ разбить его на подзапросы, чтобы он был быстрее?
- Может быть, более поздняя версия Postgres лучше? (Я попробовал 9.4, и он не кажется быстрее: все тот же эффект. Может быть, более поздняя версия?)
- Может быть, нужна другая стратегия индексации?
unaccent
неизменным. Я добавил это к вопросу.
unaccent
модуля. Одна из причин, почему я предлагаю вместо этого функцию-оболочку.
unaccent()
он также предоставляется дополнительным модулем, и Postgres по умолчанию не поддерживает индексы для функции, поскольку это не такIMMUTABLE
. Вы, должно быть, что-то изменили, и вы должны упомянуть, что именно сделали в своем вопросе. Мой постоянный совет: stackoverflow.com/a/11007216/939860 . Кроме того, индексы триграммы поддерживают регистронезависимое соответствие из коробки. Вы можете упростить до:WHERE f_unaccent(label) ILIKE f_unaccent('%someword%')
- с соответствующим индексом. Подробности: stackoverflow.com/a/28636000/939860 .