Полнотекстовый запрос Postgres очень медленный (индекс GIN, 3 миллиона строк)

#postgresql #full-text-search

#postgresql #полнотекстовый поиск

Вопрос:

ИСПОЛЬЗОВАНИЕ ПРОСТОГО ПОЛНОТЕКСТОВОГО ЗАПРОСА:

Я использовал только столбец, который индексируется в SELECT, чтобы избежать дополнительных операций чтения с диска.

 EXPLAIN (ANALYZE,BUFFERS)
SELECT products_vector
FROM products
WHERE
products_vector @@ to_tsquery('simple', 'sulfuric | acid | 0.1n');
  

Результат:

 Bitmap Heap Scan on products  (cost=2190.65..295693.90 rows=116987 width=148) (actual time=715.964..315781.144 rows=111681 loops=1)
   Recheck Cond: (products_vector @@ '''sulfuric'' | ''acid'' | ''0.1'' amp; ''n'''::tsquery)
   Heap Blocks: exact=84740
   Buffers: shared hit=6 read=85492
   ->  Bitmap Index Scan on idx_fulltext_search  (cost=0.00..2161.40 rows=116987 width=0) (actual time=643.421..643.428 rows=112306 loops=1)
         Index Cond: (products_vector @@ '''sulfuric'' | ''acid'' | ''0.1'' amp; ''n'''::tsquery)
         Buffers: shared hit=6 read=322
 Planning Time: 808.165 ms
 JIT:
   Functions: 4
   Options: Inlining false, Optimization false, Expressions true, Deforming true
   Timing: Generation 2.253 ms, Inlining 0.000 ms, Optimization 2.188 ms, Emission 24.550 ms, Total 28.991 ms
 Execution Time: 316028.302 ms
(13 rows)
  

ОГРАНИЧЕНИЕ КОЛИЧЕСТВА ИЗВЛЕКАЕМЫХ СТРОК (но добавляется ранжирование):

 EXPLAIN (ANALYZE,BUFFERS)
SELECT * FROM (
    SELECT
        id,
        ts_rank(products_vector, to_tsquery('simple', 'sulfuric | acid | 0.1n')  ) as score
    FROM products
    WHERE
        products_vector @@ to_tsquery('simple', 'sulfuric | acid | 0.1n')
) as t1
ORDER BY score DESC
LIMIT 20 OFFSET 0;
  

Результат:

 Limit  (cost=299099.35..299099.40 rows=20 width=8) (actual time=755802.185..755802.190 rows=20 loops=1)
   Buffers: shared hit=364399 read=140370
   ->  Sort  (cost=299099.35..299391.82 rows=116987 width=8) (actual time=755248.597..755248.600 rows=20 loops=1)
         Sort Key: (ts_rank(products.products_vector, '''sulfuric'' | ''acid'' | ''0.1'' amp; ''n'''::tsquery)) DESC
         Sort Method: top-N heapsort  Memory: 26kB
         Buffers: shared hit=364399 read=140370
         ->  Bitmap Heap Scan on products  (cost=2190.65..295986.37 rows=116987 width=8) (actual time=1144.787..755097.254 rows=111681 loops=1)
               Recheck Cond: (products_vector @@ '''sulfuric'' | ''acid'' | ''0.1'' amp; ''n'''::tsquery)
               Heap Blocks: exact=84740
               Buffers: shared hit=364396 read=140370
               ->  Bitmap Index Scan on idx_fulltext_search  (cost=0.00..2161.40 rows=116987 width=0) (actual time=956.063..956.064 rows=112306 loops=1)
                     Index Cond: (products_vector @@ '''sulfuric'' | ''acid'' | ''0.1'' amp; ''n'''::tsquery)
                     Buffers: shared hit=6 read=322
 Planning Time: 728.459 ms
 JIT:
   Functions: 6
   Options: Inlining false, Optimization false, Expressions true, Deforming true
   Timing: Generation 2.352 ms, Inlining 0.000 ms, Optimization 184.210 ms, Emission 358.190 ms, Total 544.751 ms
 Execution Time: 757308.337 ms
  

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

Выполнение даже запроса с использованием to_tsquery(‘simple’, ‘sulphuric amp; acid amp; 0.1n’) занимает более минуты.

Мои вопросы:

  1. Как мне ускорить эти запросы?
  2. Является ли «сканирование кучи растровых изображений» медленным из-за неправильных настроек? В таком случае, как мне их идентифицировать?
  3. Я пытался исключить «сканирование кучи растровых изображений», не выбирая в первом запросе никакой другой столбец, кроме product_vector? Почему он все еще медленный?

Заранее спасибо

Ответ №1:

Я использовал только столбец, который индексируется в SELECT, чтобы избежать дополнительных операций чтения с диска.

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

Что произойдет, если вы даже не выберете индексированный столбец, например count(*) ? Если вы выполняете значение несколько раз подряд, каково время повторного выполнения? Какие планы вы получите, если включите track_io_timing ?

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

Почему бы и нет?

Выполнение даже запроса с использованием to_tsquery(‘simple’, ‘sulphuric amp; acid amp; 0.1n’) занимает более минуты.

Что такое EXPLAIN (ANALYZE, BUFFERS) track_io_timing включенным) для этого?