хэш-соединения вызывают ошибку «не осталось места»

#postgresql

#postgresql

Вопрос:

В PostgreSQL 11 на AWS есть запрос mview в этой базе данных OLAP, задействованные таблицы огромны — в среднем 50-100 миллионов записей в большинстве случаев содержат сотни столбцов. Запрос выполняется некоторое время, а затем выдает ошибку «На устройстве не осталось свободного места». Я мог видеть, что он генерирует около 500 ГБ данных временного файла. Иногда это выполняется, а иногда завершается сбоем — вероятно, из-за других запросов, выполняемых одновременно и вызывающих сбой.

Таблицы разбиты на разделы и проиндексированы в PKs и FKS. Использование параллелизма (4) с увеличенной work_mem (4 ГБ).

Соединения происходят примерно в 10 таблицах, и все они объединяются в столбцах PK и FK. Я вижу, что происходит сокращение раздела, но хэш-соединения прерывают запрос.

Есть ли какой-либо способ избежать хэш-соединений? Если мы создадим хэш-индексы для объединяемых столбцов, будет ли PostgreSQL избегать операции хэширования и вместо этого использовать хэш-индексы для таблиц и объединять их. Таким образом, я чувствую, что можно избежать ресурсоемкого хэширования и не будет необходимости во временных файлах. Я пытался, но, похоже, не работает, когда я запрашиваю таблицу с определенными значениями, тогда она использует хэш-индекс, но когда я соединяю таблицы, кажется, что он выполняет свое собственное хэш-соединение.

Мой вопрос заключается в том, как оптимизировать массовые объединения таблиц в PostgreSQL для устранения сбоев в пространстве и ускорить его выполнение — сейчас на это уходит пара часов. Любые рекомендации или предложения.

     SELECT
   ...
   250  cols from various below tables
   ...
FROM
        x.table1 yankee_charlie
INNER JOIN x.juliet_juliet juliet_alpha ON
        yankee_charlie.bravo_tango = juliet_alpha.kilo_four
LEFT OUTER JOIN y.sierra_delta kilo_foxtrot ON
        yankee_charlie.sierra_six = kilo_foxtrot.november_juliet
LEFT OUTER JOIN z.xray_bravo uniform_delta ON
        yankee_charlie.alpha_four = uniform_delta.papa_mike
LEFT OUTER JOIN x.papa_whiskey india_five ON
        yankee_charlie.golf = india_five.tango_mike
LEFT OUTER JOIN x.lima_romeo hotel ON
        yankee_charlie.zulu_oscar = hotel.bravo_hotel
LEFT OUTER JOIN x.romeo_golf foxtrot_whiskey_two ON
        yankee_charlie.xray_alpha = foxtrot_whiskey_two.tango_quebec
LEFT OUTER JOIN a.seven_yankee zulu_four ON
        yankee_charlie.uniform_india = zulu_four.seven_bravo
LEFT OUTER JOIN a.quebec_november kilo_lima ON
        zulu_four.mike_four = kilo_lima.lima_uniform
LEFT OUTER JOIN a.tango_romeo romeo_xray_echo ON
        kilo_lima.lima_uniform = romeo_xray_echo.lima_uniform
LEFT OUTER JOIN b.seven_three four_hotel ON
        kilo_lima.zulu_three = four_hotel.whiskey_victor_bravo
LEFT OUTER JOIN x.juliet_yankee five_quebec ON
        yankee_charlie.oscar_india = five_quebec.six_xray
LEFT OUTER JOIN z.romeo_two delta_mike ON
        yankee_charlie.four_zulu = delta_mike.whiskey_victor_seven
LEFT OUTER JOIN z.delta_lima six_alpha ON
        yankee_charlie.xray_three = six_alpha.kilo_whiskey
LEFT OUTER JOIN x.five_hotel xray_quebec ON
        yankee_charlie.bravo_tango = xray_quebec.kilo_four
LEFT OUTER JOIN y.sierra_delta mike_foxtrot ON
        yankee_charlie.two = mike_foxtrot.november_juliet
LEFT OUTER JOIN y.sierra_delta india_three ON
        yankee_charlie.victor = india_three.november_juliet
WHERE
        yankee_charlie.romeo_xray_two >= (CURRENT_DATE - INTERVAL '5 years')
        AND yankee_charlie.romeo_xray_two < papa_five('year',(CURRENT_DATE   INTERVAL '1 year')) - INTERVAL '1 day';

Gather  (cost=33464846.41..475412138.09 rows=97965031 width=7161)
    Workers Planned: 2
  ->  Parallel Hash Left Join  (cost=33463846.41..465614634.99 rows=40818763 width=7161)
          Hash Cond: (yankee_charlie.victor = india_three.november_juliet)
        ->  Parallel Hash Left Join  (cost=33330811.86..392519286.24 rows=40818763 width=7109)
                Hash Cond: (yankee_charlie.two = mike_foxtrot.november_juliet)
              ->  Hash Left Join  (cost=33197777.31..321716804.91 rows=40818763 width=7056)
                      Hash Cond: (yankee_charlie.xray_three = six_alpha.kilo_whiskey)
                    ->  Hash Left Join  (cost=33197713.71..321608781.15 rows=40818763 width=7003)
                            Hash Cond: (yankee_charlie.four_zulu = delta_mike.whiskey_victor_seven)
                          ->  Hash Left Join  (cost=33197035.05..321500899.07 rows=40818763 width=6863)
                                  Hash Cond: (yankee_charlie.oscar_india = five_quebec.six_xray)
                                ->  Parallel Hash Left Join  (cost=33196883.64..321393345.56 rows=40818763 width=6813)
                                        Hash Cond: (yankee_charlie.bravo_tango = xray_quebec.kilo_four)
                                      ->  Parallel Hash Left Join  (cost=29850433.79..255866124.43 rows=40818763 width=6125)
                                              Hash Cond: (zulu_four.mike_four = kilo_lima.lima_uniform)
                                            ->  Hash Left Join  (cost=27572665.00..192250116.73 rows=40818763 width=6070)
                                                    Hash Cond: (yankee_charlie.zulu_oscar = hotel.bravo_hotel)
                                                  ->  Parallel Hash Left Join  (cost=27571519.35..192141780.40 rows=40818763 width=6042)
                                                          Hash Cond: (yankee_charlie.alpha_four = uniform_delta.papa_mike)
                                                        ->  Parallel Hash Join  (cost=27569303.10..192032398.49 rows=40818763 width=5775)
                                                                Hash Cond: (yankee_charlie.bravo_tango = foxtrot_whiskey_bravo2.kilo_four)
                                                              ->  Parallel Hash Left Join  (cost=3696445.91..128666530.60 rows=40818763 width=2497)
                                                                      Hash Cond: (yankee_charlie.sierra_six = kilo_foxtrot.november_juliet)
                                                                    ->  Parallel Hash Left Join  (cost=3550202.36..106708533.27 rows=40818763 width=2147)
                                                                            Hash Cond: (yankee_charlie.xray_alpha = foxtrot_whiskey_two.tango_quebec)
                                                                          ->  Parallel Hash Left Join  (cost=1366012.90..84660500.52 rows=40818763 width=1926)
                                                                                  Hash Cond: (yankee_charlie.uniform_india = zulu_four.seven_bravo)
                                                                                ->  Parallel Hash Left Join  (cost=3031.30..65010702.64 rows=40818763 width=1781)
                                                                                        Hash Cond: (yankee_charlie.golf = india_five.tango_mike)
                                                                                      ->  Parallel Append  (cost=0.12..64900513.56 rows=40818780 width=1835)
                                                                                              Subplans Removed: 25
                                                                                            ->  Parallel Index Scan using november_mike on quebec_victor yankee_charlie  (cost=0.12..8.15 rows=1 width=10798)
                                                                                                    Filter: ((romeo_xray_two >= (CURRENT_DATE - 'uniform_tango'::interval)) AND (romeo_xray_two < (papa_five('quebec_four'::text, (CURRENT_DATE   'foxtrot_juliet'::interval)) - 'seven_oscar'::interval)))
                                                                                            ->  Parallel Index Scan using alpha_six on xray_foxtrot romeo_oscar  (cost=0.12..8.15 rows=1 width=10798)
                                                                                                    Filter: ((romeo_xray_two >= (CURRENT_DATE - 'uniform_tango'::interval)) AND (romeo_xray_two < (papa_five('quebec_four'::text, (CURRENT_DATE   'foxtrot_juliet'::interval)) - 'seven_oscar'::interval)))
                                                                                            ->  Parallel Index Scan using whiskey_three on yankee_bravo charlie  (cost=0.12..8.15 rows=1 width=10798)
                                                                                                    Filter: ((romeo_xray_two >= (CURRENT_DATE - 'uniform_tango'::interval)) AND (romeo_xray_two < (papa_five('quebec_four'::text, (CURRENT_DATE   'foxtrot_juliet'::interval)) - 'seven_oscar'::interval)))
                                                                                            ->  Parallel Index Scan using quebec_india on five_yankee quebec_foxtrot  (cost=0.12..8.15 rows=1 width=10798)
                                                                                                    Filter: ((romeo_xray_two >= (CURRENT_DATE - 'uniform_tango'::interval)) AND (romeo_xray_two < (papa_five('quebec_four'::text, (CURRENT_DATE   'foxtrot_juliet'::interval)) - 'seven_oscar'::interval)))
                                                                                            ->  Parallel Seq Scan on romeo_zulu oscar_seven  (cost=0.00..48358699.80 rows=30351009 width=1975)
                                                                                                    Filter: ((romeo_xray_two >= (CURRENT_DATE - 'uniform_tango'::interval)) AND (romeo_xray_two < (papa_five('quebec_four'::text, (CURRENT_DATE   'foxtrot_juliet'::interval)) - 'seven_oscar'::interval)))
                                                                                            ->  Parallel Seq Scan on xray_seven five_golf  (cost=0.00..16337483.57 rows=10467742 width=1428)
                                                                                                    Filter: ((romeo_xray_two >= (CURRENT_DATE - 'uniform_tango'::interval)) AND (romeo_xray_two < (papa_five('quebec_four'::text, (CURRENT_DATE   'foxtrot_juliet'::interval)) - 'seven_oscar'::interval)))
                                                                                      ->  Parallel Hash  (cost=2643.30..2643.30 rows=31030 width=76)
                                                                                            ->  Parallel Seq Scan on papa_whiskey india_five  (cost=0.00..2643.30 rows=31030 width=76)
                                                                                ->  Parallel Hash  (cost=1131848.93..1131848.93 rows=5529893 width=210)
                                                                                      ->  Parallel Seq Scan on seven_yankee zulu_four  (cost=0.00..1131848.93 rows=5529893 width=210)
                                                                          ->  Parallel Hash  (cost=1797395.54..1797395.54 rows=6623354 width=351)
                                                                                ->  Parallel Seq Scan on romeo_golf foxtrot_whiskey_two  (cost=0.00..1797395.54 rows=6623354 width=351)
                                                                    ->  Parallel Hash  (cost=122037.80..122037.80 rows=365580 width=415)
                                                                          ->  Parallel Seq Scan on sierra_delta kilo_foxtrot  (cost=0.00..122037.80 rows=365580 width=415)
                                                              ->  Parallel Hash  (cost=10995540.28..10995540.28 rows=30397353 width=3343)
                                                                    ->  Parallel Append  (cost=0.00..10995540.28 rows=30397353 width=3343)
                                                                          ->  Parallel Seq Scan on kilo_november lima_five  (cost=0.00..8362872.25 rows=22396025 width=3681)
                                                                          ->  Parallel Seq Scan on five_hotel foxtrot_oscar  (cost=0.00..2480681.27 rows=8001327 width=2398)
                                                                          ->  Parallel Seq Scan on juliet_juliet juliet_alpha  (cost=0.00..0.00 rows=1 width=7566)
                                                        ->  Parallel Hash  (cost=2012.78..2012.78 rows=16278 width=332)
                                                              ->  Parallel Seq Scan on xray_bravo uniform_delta  (cost=0.00..2012.78 rows=16278 width=332)
                                                  ->  Hash  (cost=1008.62..1008.62 rows=10962 width=158)
                                                        ->  Seq Scan on lima_romeo hotel  (cost=0.00..1008.62 rows=10962 width=158)
                                            ->  Parallel Hash  (cost=2112348.26..2112348.26 rows=4256042 width=185)
                                                  ->  Hash Left Join  (cost=671798.94..2112348.26 rows=4256042 width=185)
                                                          Hash Cond: (kilo_lima.zulu_three = four_hotel.whiskey_victor_bravo)
                                                        ->  Parallel Hash Left Join  (cost=670990.36..2100358.89 rows=4256042 width=188)
                                                                Hash Cond: (kilo_lima.lima_uniform = romeo_xray_echo.lima_uniform)
                                                              ->  Parallel Seq Scan on quebec_november kilo_lima  (cost=0.00..1156375.42 rows=4256042 width=176)
                                                              ->  Parallel Hash  (cost=563811.27..563811.27 rows=4253927 width=77)
                                                                    ->  Parallel Seq Scan on tango_romeo romeo_xray_echo  (cost=0.00..563811.27 rows=4253927 width=77)
                                                        ->  Hash  (cost=740.48..740.48 rows=5448 width=127)
                                                              ->  Seq Scan on seven_three four_hotel  (cost=0.00..740.48 rows=5448 width=127)
                                      ->  Parallel Hash  (cost=2480681.27..2480681.27 rows=8001327 width=753)
                                            ->  Parallel Seq Scan on five_hotel xray_quebec  (cost=0.00..2480681.27 rows=8001327 width=753)
                                ->  Hash  (cost=128.96..128.96 rows=1796 width=180)
                                      ->  Seq Scan on juliet_yankee five_quebec  (cost=0.00..128.96 rows=1796 width=180)
                          ->  Hash  (cost=573.85..573.85 rows=8385 width=270)
                                ->  Seq Scan on romeo_two delta_mike  (cost=0.00..573.85 rows=8385 width=270)
                    ->  Hash  (cost=56.60..56.60 rows=560 width=183)
                          ->  Seq Scan on delta_lima six_alpha  (cost=0.00..56.60 rows=560 width=183)
              ->  Parallel Hash  (cost=122037.80..122037.80 rows=365580 width=118)
                    ->  Parallel Seq Scan on sierra_delta mike_foxtrot  (cost=0.00..122037.80 rows=365580 width=118)
        ->  Parallel Hash  (cost=122037.80..122037.80 rows=365580 width=118)
              ->  Parallel Seq Scan on sierra_delta india_three  (cost=0.00..122037.80 rows=365580 width=118)
  

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

1. «Я мог видеть, как он генерирует около 500 ГБ данных временного файла» Вы имеете в виду, что вы действительно можете наблюдать, как он генерирует столько, или что вы можете представить, как он это делает?

2. Вы можете запретить хэш-соединения, выполнив set enable_hashjoin = off но все, что он делает вместо хэш-соединения, вероятно, будет еще хуже.

3. Вы выбираете сотни столбцов из каждой таблицы в десятиходовом соединении? Или большинство из них остаются невыбранными в любом данном соединении?

4. @jjanes — Я видел, что он сгенерировал временный файл объемом 500 ГБ, да, как вы сказали, запрет хэша усугубил ситуацию — вложенный цикл и объединение слиянием были слишком дорогими, чем хэш. Что касается количества столбцов, да, извлекается довольно много столбцов для каждой таблицы.

5. @a_horse_with_no_name добавил запрос и план выполнения.

Ответ №1:

Запрос заработал, выделив большее пространство work_mem (16 ГБ) и parallel = 4, на сервере достаточно места (400 ГБ) для поддержки сеанса с большим объемом памяти. Из-за этого запрос не передавался во временные файлы и выполнялся намного быстрее.