2016-09-30 4 views
1

Я пытался быть тщательными в этом вопросе, так что если вы нетерпеливы, просто прыгать до конца, чтобы увидеть, что фактический вопрос ...Postgresql КАК ЛЮБОЙ против LIKE

Я работаю над настройка некоторых функций поиска в одной из наших баз данных. С этой целью я добавляю некоторые подстановочные возможности API нашего приложения, которые обращаются к Postgresql.

Проблема, которую я обнаружил, заключается в том, что EXPLAIN ANALYZE раз не имеет смысла для меня, и я пытаюсь выяснить, где я могу ошибиться; похоже, что 15 запросов лучше, чем только один оптимизированный запрос!

Таблица, Words, имеет две соответствующие колонки по этому вопросу: id и text. Столбец text имеет индекс, который был построен с опцией text_pattern_ops. Вот что я вижу:

Во-первых, с помощью LIKE ANY с положением в VALUES, который some references, кажется, показывают, было бы идеально в моем случае (найти несколько слов):

events_prod=# explain analyze select distinct id from words where words.text LIKE ANY (values('test%')); 
                   QUERY PLAN                
-------------------------------------------------------------------------------------------------------------------------------------- 
HashAggregate (cost=6716668.40..6727372.85 rows=1070445 width=4) (actual time=103088.381..103091.468 rows=256 loops=1) 
    Group Key: words.id 
    -> Nested Loop Semi Join (cost=0.00..6713992.29 rows=1070445 width=4) (actual time=0.670..103087.904 rows=256 loops=1) 
     Join Filter: ((words.text)::text ~~ "*VALUES*".column1) 
     Rows Removed by Join Filter: 214089311 
     -> Seq Scan on words (cost=0.00..3502655.91 rows=214089091 width=21) (actual time=0.017..25232.135 rows=214089567 loops=1) 
     -> Materialize (cost=0.00..0.02 rows=1 width=32) (actual time=0.000..0.000 rows=1 loops=214089567) 
       -> Values Scan on "*VALUES*" (cost=0.00..0.01 rows=1 width=32) (actual time=0.006..0.006 rows=1 loops=1) 
Planning time: 0.226 ms 
Execution time: 103106.296 ms 
(10 rows) 

Как вы можете видеть, время исполнения является ужасным.

Вторая попытка, используя LIKE ANY(ARRAY[... выходы:

events_prod=# explain analyze select distinct id from words where words.text LIKE ANY(ARRAY['test%']); 
                QUERY PLAN              
--------------------------------------------------------------------------------------------------------------------- 
HashAggregate (cost=3770401.08..3770615.17 rows=21409 width=4) (actual time=37399.573..37399.704 rows=256 loops=1) 
    Group Key: id 
    -> Seq Scan on words (cost=0.00..3770347.56 rows=21409 width=4) (actual time=0.224..37399.054 rows=256 loops=1) 
     Filter: ((text)::text ~~ ANY ('{test%}'::text[])) 
     Rows Removed by Filter: 214093922 
Planning time: 0.611 ms 
Execution time: 37399.895 ms 
(7 rows) 

Как вы можете видеть, производительность значительно улучшилась, но все еще далеко от идеала ... 37 секунд. с одним словом в списке. Перемещение до трех слов, которое возвращает в общей сложности 256 строк, изменяет время выполнения на более чем 100 секунд.

Последняя попытка, делает LIKE для одного слова:

events_prod=# explain analyze select distinct id from words where words.text LIKE 'test%'; 
                  QUERY PLAN                
------------------------------------------------------------------------------------------------------------------------------------- 
HashAggregate (cost=60.14..274.23 rows=21409 width=4) (actual time=1.437..1.576 rows=256 loops=1) 
    Group Key: id 
    -> Index Scan using words_special_idx on words (cost=0.57..6.62 rows=21409 width=4) (actual time=0.048..1.258 rows=256 loops=1) 
     Index Cond: (((text)::text ~>=~ 'test'::text) AND ((text)::text ~<~ 'tesu'::text)) 
     Filter: ((text)::text ~~ 'test%'::text) 
Planning time: 0.826 ms 
Execution time: 1.858 ms 
(7 rows) 

Как и следовало ожидать, это самый быстрый, но 1.85ms заставляет меня задаться вопросом, есть ли что-то еще, что я пропускаю с VALUES и ARRAY подход.

Вопрос

Есть ли более эффективный способ сделать что-то подобное в Postgresql, что я пропустил в своих исследованиях?

select distinct id 
    from words 
    where words.text LIKE ANY(ARRAY['word1%', 'another%', 'third%']); 
+0

Вы проверили регулярное выражение или версию «похоже на»? Что-то вроде 'words.text ~ '^ (word1 | another | third)' или' words.text похоже на '(word1 | another | third)% '. –

+0

Да. Первая версия, которую вы предлагаете, анализирует более чем на 202 секунды. Все мои исследования говорят, чтобы избежать «ПОДОБНОГО ТОКА» любой ценой, и это также подтверждается анализом результатов за 197 секунд, только перед регулярным выражением. –

+1

Считаете ли вы использование [полного текстового индекса] (https://www.postgresql.org/docs/current/static/textsearch.html)? –

ответ

2

Это немного умозрительно. Я думаю, что ключ ваша картина:

where words.text LIKE 'test%' 

Обратите внимание, что like шаблона начинается с постоянной строкой. Это означает, что Postgres может выполнять сканирование диапазона по индексу для слов, начинающихся с 'test'.

Когда вы вводите несколько сравнений, оптимизатор запутывается и больше не рассматривает многократное сканирование диапазона. Вместо этого он решает, что ему необходимо обработать все строки.

Это может быть случай, когда это переписана дает производительность, что вы хотите:

select id 
from words 
where words.text LIKE 'word1%' 
union 
select id 
from words 
where words.text LIKE 'another%' 
union 
select id 
from words 
where words.text LIKE 'third%'; 

Примечания:

  • distinct не требуется из-за union.
  • Если шаблон начинается с подстановочного знака, то в любом случае требуется полное сканирование.
  • Возможно, вам стоит рассмотреть n-грамм или полнотекстовый индекс таблицы.
+0

Это очень интересно ... Союз работает на 0,93 мс –

+0

Мне любопытно, есть ли способ лучше проинструктировать оптимизатора, не делая много союзов ... Хотя это не плохое решение, похоже, что это неправильный подход к проблеме с точки зрения SQL. –

+0

Возможно, вы можете попробовать 'WHERE words.text LIKE 'word1%' ИЛИ ​​words.text LIKE 'another%' OR ...'. Таким образом, вы можете завершить сканирование растрового индекса, которое медленнее, чем сканирование индекса, но быстрее, чем последовательное сканирование. –

Смежные вопросы