Возможно ли сократить время выполнения этого запроса PostgreSQL с 50 мс до порядка нескольких мс?

#sql #postgresql #performance

#sql #postgresql #Производительность

Вопрос:

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

 explain analyze
            select
                *
            from
                rtsepezocoav_102999 av
            join rtco_102097 c on
                c.lo_id = av.co_id
            where
                av.ad_id = 335
                and av.pe_id = 70
                and av.se_id = 12
  

Я получаю следующий план:

 Hash Join  (cost=1238.88..3275.61 rows=40581 width=80) (actual time=10.341..47.707 rows=41238 loops=1)
  Hash Cond: (av.co_id = c.lo_id)
  ->  Bitmap Heap Scan on rtsepezocoav_p_70_103719 av  (cost=868.38..2798.54 rows=40581 width=68) (actual time=4.550..17.615 rows=41238 loops=1)
        Recheck Cond: ((se_id = 12) AND (ad_id = 335))
        Filter: (pe_id = 70)
        Heap Blocks: exact=360
        ->  Bitmap Index Scan on rtsepezocoav_p_70_103719_se_id_ad_id_idx  (cost=0.00..858.23 rows=40581 width=0) (actual time=4.450..4.450 rows=41238 loops=1)
              Index Cond: ((se_id = 12) AND (ad_id = 335))
  ->  Hash  (cost=204.67..204.67 rows=13267 width=12) (actual time=5.759..5.759 rows=13267 loops=1)
        Buckets: 16384  Batches: 1  Memory Usage: 699kB
        ->  Seq Scan on rtco_102097 c  (cost=0.00..204.67 rows=13267 width=12) (actual time=0.009..2.125 rows=13267 loops=1)
Planning Time: 0.515 ms
Execution Time: 51.100 ms
  

Я думаю, это не страшно. Но фактический запрос более сложный я буду запускать этот запрос параллельно на разных сегментах. Итак, я действительно сосредоточен на том, чтобы получить это молниеносно. Есть ли что-то, чего мне не хватает, или это просто предел postgres? На мой взгляд, это кажется немного «медленным» — объединять 40 тысяч записей (rtsepezocoav_p_70_103719) с 10 тысячами записей (rtco).

rtsepezocoav_p_70_103719 имеет индексы: (ad_id, pe_id, se_id) (ad_id, se_id) (co_id)

rtco имеет индекс (lo_id)

Одна из первых вещей, которая приходит на ум, почему он использует «Сканирование кучи растровых изображений» вместо сканирования только по индексу?

Когда я делаю:

 explain analyze select 1 from rtsepezocoav_102999 av
  

Я получаю сканирование только по индексу, и оно занимает 15 мс (также кажется длинным).

Я на Postgres 12.

Комментарии:

1. Он возвращает что-то вроде 40 тыс. строк. Это займет время.

2. Время выполнения быстрее, чем вы моргаете глазом. Сколько именно МС вы ожидаете и чего пытаетесь достичь?

3. Можете ли вы показать нам план, созданный с помощью explain (analyze, buffers, timing) и запущенный set track_io_timing=on; перед этим (для этого вам потребуется доступ суперпользователя). Вы также можете попробовать поиграть с порядком столбцов для индекса, rtsepezocoav_p_70_103719 возможно, помещенного pe_id в качестве первого столбца. Какую версию Postgres вы используете?

4. есть ли у rtsepezocoav_102999 индекс в co_id? как правило, вы хотели бы, чтобы индексировались обе стороны условия объединения

5. Вы не сможете легко получить сканирование только по индексу, если вы SELECT * .

Ответ №1:

Я думаю, что вы могли бы добавить индекс на co_id . У вас есть индексы во всех идентификаторах в where, но при объединении у вас есть индекс только на 1 стороне объединения.

 rtco has index on (lo_id)

rtsepezocoav_p_70_103719 has indexes on: (ad_id, pe_id, se_id) => this one should have index on co_id 
  

При отсутствии индекса в co_id он в конечном итоге выполняет последовательное сканирование rtsepezocoav_p_70_103719 таблицы при объединении

Комментарии:

1. Упс, я пропустил сообщить о некоторых других индексах, которые у меня есть, включая тот, который вы упомянули. Я обновлю вопрос.

2. В таком случае, это всего лишь мысль, но, возможно, если вы сначала отфильтруете таблицу rtsepezocoav_p_70_103719 с помощью «где av.ad_id = 335 и av.pe_id = 70 и av.se_id = 12, а затем выполните объединение с меньшим количеством строк на этой стороне, это могло бы помочь. В этот момент запрос сначала выполняет from , затем join, а затем where, так что в конечном итоге он выполняет join с некоторыми строками, которые не имеют отношения к делу, а затем использует where для фильтрации соответствующих результатов

Ответ №2:

Спасибо за советы, но, честно говоря, я думаю, что я действительно выполнял преждевременную оптимизацию…