Как интерпретировать результаты объяснения PostgreSQL при зависании запроса

#postgresql

#postgresql

Вопрос:

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

Для открывателей, по причинам, в которые я не буду вдаваться, я нормализовал длинные абзацы в таблицу с именем shared.notes .

Далее у меня есть сложное представление с несколькими поисками абзацев. Каждое поле note_id (а) индексируется и (б) имеет ограничение внешнего ключа для таблицы notes. Псевдокод ниже:

 CREATE VIEW shared.vw_get_the_whole_kit_and_kaboodle AS
SELECT
    yada yada

  , mi.electrical_note_id
  , electrical_notes.note AS electrical_notes
  , mi.hvac_note_id
  , hvac_notes.note AS hvac_notes
  , mi.network_note_id
  , network_notes.note AS network_notes
  , mi.plumbing_note_id
  , plumbing_notes.note AS plumbing_notes
  , mi.specification_note_id
  , specification_notes.note AS specification_notes
  , mi.structural_note_id
  , structural_notes.note AS structural_notes

FROM shared.a_table AS mi
JOIN shared.generic_items AS gi
  ON mi.generic_item_id = gi.generic_item_id
JOIN shared.manufacturers AS mft
  ON mi.manufacturer_id = mft.manufacturer_id
JOIN shared.notes AS electrical_notes
  ON mi.electrical_note_id = electrical_notes.note_id
JOIN shared.notes AS hvac_notes
  ON mi.hvac_note_id = hvac_notes.note_id
JOIN shared.notes AS plumbing_notes
  ON mi.plumbing_note_id = plumbing_notes.note_id
JOIN shared.notes AS specification_notes
  ON mi.specification_note_id = specification_notes.note_id
JOIN shared.notes AS structural_notes
  ON mi.structural_note_id = structural_notes.note_id
JOIN shared.notes AS network_notes
  ON mi.network_note_id = network_notes.note_id
JOIN shared.connectivity AS nc
  ON mi.connectivity_id = nc.connectivity_id
WHERE
  mi.deletion_date IS NULL;
 

Затем я выбираю против этого представления:

 SELECT
  lots of columns...
FROM shared.vw_get_the_whole_kit_and_kaboodle
WHERE 
  is_active = TRUE
  AND is_inventory = FALSE;
 

Как ни странно, в облачных базах данных GCP я еще не сталкивался с проблемами, и в некоторых из этих таблиц задействованы тысячи строк.

Тем временем на ранчо, на моем локальном компьютере, у меня есть тестовая версия базы данных. ТОТ ЖЕ САМЫЙ SQL, вплоть до последней буквы. Поверьте мне в этом. Для определения таблиц, определения представлений, индексы… все.

Облако будет возвращать запросы почти мгновенно.

Локальный КОМПЬЮТЕР зависнет — и это несмотря на то, что база данных ПК содержит всего несколько строк в разных таблицах. Итак, если кто-то должен зависнуть, это должны быть облачные базы данных. Но все наоборот; база данных с небольшим набором данных — это та, которая выходит из строя.

Добавьте этот поворот сюжета: если я удалю фильтр для is_inventory, запрос на ПК вернется мгновенно. Кроме того, если я просто удаляю один за другим соединения с таблицей заметок, после того, как примерно половина из них исчезнет, КОМПЬЮТЕР начнет завершаться мгновенно. Похоже, что это расстроено, что так много раз попадать в одну и ту же таблицу одним запросом.

Если я запускаю EXPLAIN (без ANALYZE опции), вот версия БЕЗ зависания:

 Hash Left Join  (cost=31.55..40.09 rows=43 width=751)
 Hash Cond: (mi.mounting_location_id = ml.mounting_location_id)
 ->  Hash Left Join  (cost=30.34..38.76 rows=43 width=719)
  Hash Cond: (mi.price_type_id = pt.price_type_id)
  ->  Hash Join  (cost=29.25..37.53 rows=43 width=687)
   Hash Cond: (mi.connectivity_id = nc.connectivity_id)
   ->  Nested Loop  (cost=28.16..36.21 rows=43 width=655)
    Join Filter: (mi.network_note_id = network_notes.note_id)
    ->  Seq Scan on notes network_notes  (cost=0.00..1.01 rows=1 width=48)
    ->  Nested Loop  (cost=28.16..34.66 rows=43 width=623)
     Join Filter: (mi.plumbing_note_id = plumbing_notes.note_id)
     ->  Seq Scan on notes plumbing_notes  (cost=0.00..1.01 rows=1 width=48)
     ->  Hash Join  (cost=28.16..33.11 rows=43 width=591)
      Hash Cond: (mi.generic_item_id = gi.generic_item_id)
      ->  Hash Join  (cost=5.11..9.95 rows=43 width=559)
       Hash Cond: (mi.structural_note_id = structural_notes.note_id)
       ->  Hash Join  (cost=4.09..8.57 rows=43 width=527)
        Hash Cond: (mi.specification_note_id = specification_notes.note_id)
        ->  Hash Join  (cost=3.07..7.37 rows=43 width=495)
         Hash Cond: (mi.hvac_note_id = hvac_notes.note_id)
         ->  Hash Join  (cost=2.04..5.99 rows=43 width=463)
          Hash Cond: (mi.electrical_note_id = electrical_notes.note_id)
          ->  Hash Join  (cost=1.02..4.70 rows=43 width=431)
           Hash Cond: (mi.manufacturer_id = mft.manufacturer_id)
           ->  Seq Scan on mft_items mi  (cost=0.00..3.44 rows=43 width=399)
            Filter: ((deletion_date IS NULL) AND is_active)
           ->  Hash  (cost=1.01..1.01 rows=1 width=48)
            ->  Seq Scan on manufacturers mft  (cost=0.00..1.01 rows=1 width=48)
          ->  Hash  (cost=1.01..1.01 rows=1 width=48)
            ->  Seq Scan on notes electrical_notes  (cost=0.00..1.01 rows=1 width=48)
         ->  Hash  (cost=1.01..1.01 rows=1 width=48)
          ->  Seq Scan on notes hvac_notes  (cost=0.00..1.01 rows=1 width=48)
        ->  Hash  (cost=1.01..1.01 rows=1 width=48)
         ->  Seq Scan on notes specification_notes  (cost=0.00..1.01 rows=1 width=48)
       ->  Hash  (cost=1.01..1.01 rows=1 width=48)
        ->  Seq Scan on notes structural_notes  (cost=0.00..1.01 rows=1 width=48)
      ->  Hash  (cost=15.80..15.80 rows=580 width=48)
       ->  Seq Scan on generic_items gi  (cost=0.00..15.80 rows=580 width=48)
   ->  Hash  (cost=1.04..1.04 rows=4 width=36)
    ->  Seq Scan on connectivity nc  (cost=0.00..1.04 rows=4 width=36)
  ->  Hash  (cost=1.04..1.04 rows=4 width=36)
   ->  Seq Scan on price_types pt  (cost=0.00..1.04 rows=4 width=36)
 ->  Hash  (cost=1.09..1.09 rows=9 width=48)
  ->  Seq Scan on mounting_locations ml  (cost=0.00..1.09 rows=9 width=48)
 

И это версия зависания:

 Hash Left Join  (cost=26.43..38.57 rows=16 width=751)
 Hash Cond: (mi.mounting_location_id = ml.mounting_location_id)
 ->  Hash Left Join  (cost=25.23..37.32 rows=16 width=719)
  Hash Cond: (mi.price_type_id = pt.price_type_id)
  ->  Hash Join  (cost=24.14..36.18 rows=16 width=687)
   Hash Cond: (mi.connectivity_id = nc.connectivity_id)
   ->  Nested Loop  (cost=23.05..35.00 rows=16 width=655)
    Join Filter: (mi.network_note_id = network_notes.note_id)
    ->  Seq Scan on notes network_notes  (cost=0.00..1.01 rows=1 width=48)
    ->  Nested Loop  (cost=23.05..33.79 rows=16 width=623)
     Join Filter: (mi.structural_note_id = structural_notes.note_id)
     ->  Seq Scan on notes structural_notes  (cost=0.00..1.01 rows=1 width=48)
     ->  Nested Loop  (cost=23.05..32.58 rows=16 width=591)
      Join Filter: (mi.electrical_note_id = electrical_notes.note_id)
      ->  Seq Scan on notes electrical_notes  (cost=0.00..1.01 rows=1 width=48)
      ->  Nested Loop  (cost=23.05..31.37 rows=16 width=559)
       Join Filter: (mi.specification_note_id = specification_notes.note_id)
       ->  Seq Scan on notes specification_notes  (cost=0.00..1.01 rows=1 width=48)
       ->  Nested Loop  (cost=23.05..30.16 rows=16 width=527)
        Join Filter: (mi.plumbing_note_id = plumbing_notes.note_id)
        ->  Seq Scan on notes plumbing_notes  (cost=0.00..1.01 rows=1 width=48)
        ->  Nested Loop  (cost=23.05..28.95 rows=16 width=495)
         Join Filter: (mi.hvac_note_id = hvac_notes.note_id)
         ->  Seq Scan on notes hvac_notes  (cost=0.00..1.01 rows=1 width=48)
         ->  Nested Loop  (cost=23.05..27.74 rows=16 width=463)
          Join Filter: (mi.manufacturer_id = mft.manufacturer_id)
          ->  Seq Scan on manufacturers mft  (cost=0.00..1.01 rows=1 width=48)
          ->  Hash Join  (cost=23.05..26.53 rows=16 width=431)
           Hash Cond: (mi.generic_item_id = gi.generic_item_id)
           ->  Seq Scan on mft_items mi  (cost=0.00..3.44 rows=16 width=399)
            Filter: ((deletion_date IS NULL) AND is_active AND (NOT is_inventory))
           ->  Hash  (cost=15.80..15.80 rows=580 width=48)
            ->  Seq Scan on generic_items gi  (cost=0.00..15.80 rows=580 width=48)
   ->  Hash  (cost=1.04..1.04 rows=4 width=36)
    ->  Seq Scan on connectivity nc  (cost=0.00..1.04 rows=4 width=36)
  ->  Hash  (cost=1.04..1.04 rows=4 width=36)
   ->  Seq Scan on price_types pt  (cost=0.00..1.04 rows=4 width=36)
 ->  Hash  (cost=1.09..1.09 rows=9 width=48)
  ->  Seq Scan on mounting_locations ml  (cost=0.00..1.09 rows=9 width=48)
 

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

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

1. Просто любопытно, что, если вы не использовали представление, но включили его в качестве CTE — запрос все еще зависает?

2. Кроме того, и, вероятно, более вероятно — что-то было заблокировано ?

3. если я использую CTE, подобный этому SELECT * FROM (WITH mi AS (SELECT * FROM shared.a_table WHERE is_active = TRUE AND is_inventory = FALSE) SELECT ... , запрос возвращается мгновенно. И в этом случае это также исключает проблемы с блокировкой, вы согласны?

4. Почти наверняка да, это должно исключить проблемы с блокировкой. Я бы все равно проверил pg_locks в другом сеансе, пока зависающий запрос будет проверяться (не должно быть ничего с предоставленным = false). Кроме того, проверьте, работает ли серверный процесс с процессором — это то, чего я ожидал бы, если это не проблема с блокировкой.

5. Нет, я не думаю , что это полностью исключает проблемы с блокировкой, это просто означает, что проблемы с блокировкой, если это то, что это такое, не возникают, если вы не используете представление (просто плевок, не считаю себя экспертом по PG с любой натяжкой).