Как понять вложенные l oop в PostgreSQL объяснения? - PullRequest
0 голосов
/ 13 февраля 2020

Пожалуйста, ответьте, большое спасибо.

В1: почему условие запроса a.id = b.id, а только сканирование индекса a.id в начале? но число l oop настолько велико?

Q2: Что делает узел 'Nested L oop' в объяснении?

happydb=# EXPLAIN (ANALYZE,VERBOSE) SELECT b.name FROM a,b WHERE a.id = b.id AND b.id < 10000;
                                                                         QUERY PLAN

-----------------------------------------------------------------------------------------------------------------------------------------
-------------------
 Gather  (cost=1000.57..174222.54 rows=18002 width=13) (actual time=5.881..3276.311 rows=19998 loops=1)
   Output: b.name
   Workers Planned: 5
   Workers Launched: 5
   ->  Nested Loop  (cost=0.56..171422.34 rows=3600 width=13) (actual time=3.189..3258.998 rows=3333 loops=6)
         Output: b.name
         Worker 0: actual time=2.591..3259.895 rows=1850 loops=1
         Worker 1: actual time=0.180..3251.631 rows=4081 loops=1
         Worker 2: actual time=1.344..3261.433 rows=555 loops=1
         Worker 3: actual time=8.603..3262.411 rows=3330 loops=1
         Worker 4: actual time=0.821..3259.297 rows=4623 loops=1
         ->  Parallel Seq Scan on public.b  (cost=0.00..141721.20 rows=3600 width=17) (actual time=1.020..3223.285 rows=3333 loops
=6)
               Output: b.id, b.name
               Filter: (b.id < 10000)
               Rows Removed by Filter: 2663334
               Worker 0: actual time=0.054..3237.921 rows=1850 loops=1
               Worker 1: actual time=0.049..3215.862 rows=4081 loops=1
               Worker 2: actual time=0.102..3236.592 rows=555 loops=1
               Worker 3: actual time=0.296..3235.327 rows=3330 loops=1
               Worker 4: actual time=0.055..3188.732 rows=4623 loops=1
         ->  Index Only Scan using idx_rock_id on public.a  (cost=0.56..8.24 rows=1 width=4) (actual time=0.008..0.010 r
ows=1 loops=19998)
               Output: a.id
               Index Cond: (a.id = b.id)
               Heap Fetches: 19998
               Worker 0: actual time=0.011..0.011 rows=1 loops=1850
               Worker 1: actual time=0.008..0.008 rows=1 loops=4081
               Worker 2: actual time=0.044..0.044 rows=1 loops=555
               Worker 3: actual time=0.007..0.007 rows=1 loops=3330
               Worker 4: actual time=0.006..0.015 rows=1 loops=4623
 Planning Time: 0.579 ms
 Execution Time: 3277.727 ms
(31 rows)


Наконец, согласно Лоренцу Руководство Альбе, план выполнения после добавления индекса намного лучше, а время выполнения было значительно сокращено.

music=# CREATE INDEX idx_b_id ON b(id);
WARNING:  concurrent insert in progress within table "b"
CREATE INDEX


music=# set max_parallel_workers_per_gather = 6;
SET
music=# EXPLAIN (ANALYZE,VERBOSE) SELECT b.name FROM a,b WHERE a.id = b.id AND b.id < 10000;
                                                                         QUERY PLAN

-----------------------------------------------------------------------------------------------------------------------------------------
-------------------
 Gather  (cost=1388.64..108804.88 rows=20599 width=13) (actual time=1.316..35.770 rows=19998 loops=1)
   Output: b.name
   Workers Planned: 3
   Workers Launched: 3
   ->  Nested Loop  (cost=388.64..105744.98 rows=6645 width=13) (actual time=0.341..27.475 rows=5000 loops=4)
         Output: b.name
         Worker 0: actual time=0.095..26.337 rows=5365 loops=1
         Worker 1: actual time=0.091..26.641 rows=5753 loops=1
         Worker 2: actual time=0.101..28.578 rows=3145 loops=1
         ->  Parallel Bitmap Heap Scan on public.b  (cost=388.08..51229.82 rows=6645 width=17) (actual time=0.298..1.265 rows=5000 loops=4)
               Output: b.id, b.name
               Recheck Cond: (b.id < 10000)
               Heap Blocks: exact=31
               Worker 0: actual time=0.044..1.111 rows=5365 loops=1
               Worker 1: actual time=0.044..1.205 rows=5753 loops=1
               Worker 2: actual time=0.049..0.681 rows=3145 loops=1
               ->  Bitmap Index Scan on idx_b_id  (cost=0.00..382.93 rows=20599 width=0) (actual time=1.012..1.012 rows=19998 loops=1)
                     Index Cond: (b.id < 10000)
         ->  Index Only Scan using idx_para_select_id on public.a  (cost=0.56..8.19 rows=1 width=4) (actual time=0.004..0.004 rows=1 loops=19998)
               Output: a.id
               Index Cond: (a.id = b.id)
               Heap Fetches: 19998
               Worker 0: actual time=0.004..0.004 rows=1 loops=5365
               Worker 1: actual time=0.003..0.004 rows=1 loops=5753
               Worker 2: actual time=0.004..0.004 rows=1 loops=3145
 Planning Time: 0.264 ms
 Execution Time: 37.025 ms
(27 rows)

1 Ответ

0 голосов
/ 13 февраля 2020

Вложенное соединение l oop работает следующим образом:

  • PostgreSQL сканирует внешнюю таблицу , в данном случае b.

  • Для каждой строки, найденной во внешней таблице, PostgreSQL сканирует внутреннюю таблицу , в данном случае a, на соответствие строк.

    Так как во внутренней таблице есть индекс условия соединения, PostgreSQL использует там сканирование индекса.

То есть при вложенном соединении l oop, только индекс в соединении можно использовать условие для внутренней таблицы.

Существует также условие проверки внешней таблицы (b.id < 10000), но оно не имеет ничего общего с соединением. Похоже, у вас нет индекса для этого столбца, поэтому используется последовательное сканирование.

Большое количество выполнений для внутреннего l oop объясняется найденными 19998 строками, сканирующими внешнюю таблицу: для каждой из этих строк сканируется внутренняя таблица.

Почти все время выполнения затрачивается на параллельное последовательное сканирование, и большинство строк отбрасывается, поэтому я предполагаю, что следующий индекс сделает запрос значительно быстрее:

CREATE INDEX ON b (id);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...