Postgresql

PostgreSQL 10 優化慢查詢性能

  • February 17, 2022

我有以下查詢:

select i.id as id, 
      i.ts_updated as updated, 
      i."name" as name, 
      cast(attr.data_norm as text) as data_norm, 
      cast(_59.json_agg as text) as _59, 
      attr.num_ports as num_ports,
      cast(_58.json_agg as text) as _58, 
      cast(_60.json_agg as text) as _60, 
      attr.frequency as frequency, 
      cast(attr.data as text) as data, 
      attr.r_ref as r_ref, 
      attr.frequency_hz as frequency_hz, 
      cast(tags_array as text) as tags
from bo_instance i 
left join attrib_touchstone attr on i.id=attr.bo_instance_id
left join ( select x_boi_tag.bo_instance_id as instance_id, 
                  json_agg(tag."name" order by tag."name") as tags_array 
           from x_boi_tag 
           left join tag on x_boi_tag.tag_id=tag.id 
           where tag.is_deleted = false 
           group by instance_id ) t on t.instance_id=i.id 
left join ( select x.bo_instance_id, 
                  json_agg(json_build_object('name', boAttr."name", 'value', v."name")) as value_list 
           from x_ia_value_list x 
           left join bo_attribute_value v on v.id=x.bo_attribute_value_id 
           left join bo_class_attribute boAttr on v.bo_class_attribute_id = boAttr.id 
           group by x.bo_instance_id ) val on val.bo_instance_id = i.id 
left join lateral ( select json_agg(value #>> '{value}' order by value #>> '{value}')  
                   from json_array_elements(val.value_list) 
                   where value #>> '{name}' = 'freq_units') as _59 on true 
left join lateral ( select json_agg(value #>> '{value}' order by value #>> '{value}')  
                   from json_array_elements(val.value_list) 
                   where value #>> '{name}' = 'parameter_type') as _58 on true 
left join lateral ( select json_agg(value #>> '{value}' order by value #>> '{value}')  
                   from json_array_elements(val.value_list) 
                   where value #>> '{name}' = 'format') as _60 on true 
where i.is_deleted=false 
 and i.bo_class_id=34 
 and true 
order by i.id desc limit 150

執行耗時 22 秒,在我看來這相當慢。

這是查詢計劃:

Limit  (cost=869494.67..870365.34 rows=150 width=285) (actual time=25585.374..25612.329 rows=150 loops=1)
 Output: i.id, i.ts_updated, i.name, ((attr.data_norm)::text), (((json_agg((json_array_elements.value #>> '{value}'::text[]) ORDER BY (json_array_elements.value #>> '{value}'::text[]))))::text), attr.num_ports, (((json_agg((json_array_elements_1.value #>> '{value}'::text[]) ORDER BY (json_array_elements_1.value #>> '{value}'::text[]))))::text), (((json_agg((json_array_elements_2.value #>> '{value}'::text[]) ORDER BY (json_array_elements_2.value #>> '{value}'::text[]))))::text), attr.frequency, ((attr.data)::text), attr.r_ref, attr.frequency_hz, ((t.tags_array)::text)
 Buffers: shared hit=38064, temp read=41731 written=63158
 ->  Nested Loop Left Join  (cost=869494.67..7900133.26 rows=1211245 width=285) (actual time=25585.372..25605.884 rows=150 loops=1)
       Output: i.id, i.ts_updated, i.name, (attr.data_norm)::text, ((json_agg((json_array_elements.value #>> '{value}'::text[]) ORDER BY (json_array_elements.value #>> '{value}'::text[]))))::text, attr.num_ports, ((json_agg((json_array_elements_1.value #>> '{value}'::text[]) ORDER BY (json_array_elements_1.value #>> '{value}'::text[]))))::text, ((json_agg((json_array_elements_2.value #>> '{value}'::text[]) ORDER BY (json_array_elements_2.value #>> '{value}'::text[]))))::text, attr.frequency, (attr.data)::text, attr.r_ref, attr.frequency_hz, (t.tags_array)::text
       Buffers: shared hit=37674, temp read=41731 written=63158
       ->  Nested Loop Left Join  (cost=869493.16..5998478.61 rows=1211245 width=1707) (actual time=25585.330..25589.575 rows=150 loops=1)
             Output: i.id, i.ts_updated, i.name, attr.data_norm, attr.num_ports, attr.frequency, attr.data, attr.r_ref, attr.frequency_hz, t.tags_array, val.value_list, (json_agg((json_array_elements.value #>> '{value}'::text[]) ORDER BY (json_array_elements.value #>> '{value}'::text[]))), (json_agg((json_array_elements_1.value #>> '{value}'::text[]) ORDER BY (json_array_elements_1.value #>> '{value}'::text[])))
             Buffers: shared hit=37674, temp read=41731 written=63158
             ->  Nested Loop Left Join  (cost=869491.65..4133161.31 rows=1211245 width=1675) (actual time=25585.311..25588.171 rows=150 loops=1)
                   Output: i.id, i.ts_updated, i.name, attr.data_norm, attr.num_ports, attr.frequency, attr.data, attr.r_ref, attr.frequency_hz, t.tags_array, val.value_list, (json_agg((json_array_elements.value #>> '{value}'::text[]) ORDER BY (json_array_elements.value #>> '{value}'::text[])))
                   Buffers: shared hit=37674, temp read=41731 written=63158
                   ->  Merge Left Join  (cost=869490.14..2267844.01 rows=1211245 width=1643) (actual time=25585.257..25586.503 rows=150 loops=1)
                         Output: i.id, i.ts_updated, i.name, attr.data_norm, attr.num_ports, attr.frequency, attr.data, attr.r_ref, attr.frequency_hz, t.tags_array, val.value_list
                         Inner Unique: true
                         Merge Cond: (i.id = val.bo_instance_id)
                         Buffers: shared hit=37674, temp read=41731 written=63158
                         ->  Merge Left Join  (cost=1046.86..1380457.54 rows=1211245 width=1611) (actual time=9.411..10.418 rows=150 loops=1)
                               Output: i.id, i.ts_updated, i.name, attr.data_norm, attr.num_ports, attr.frequency, attr.data, attr.r_ref, attr.frequency_hz, t.tags_array
                               Inner Unique: true
                               Merge Cond: (i.id = t.instance_id)
                               Buffers: shared hit=3771
                               ->  Gather Merge  (cost=1000.88..1377380.87 rows=1211245 width=1579) (actual time=7.744..14.986 rows=150 loops=1)
                                     Output: i.id, i.ts_updated, i.name, attr.data_norm, attr.num_ports, attr.frequency, attr.data, attr.r_ref, attr.frequency_hz
                                     Workers Planned: 2
                                     Workers Launched: 2
                                     Buffers: shared hit=4154
                                     ->  Nested Loop Left Join  (cost=0.85..1236573.12 rows=504685 width=1579) (actual time=1.129..1.527 rows=80 loops=3)
                                           Output: i.id, i.ts_updated, i.name, attr.data_norm, attr.num_ports, attr.frequency, attr.data, attr.r_ref, attr.frequency_hz
                                           Buffers: shared hit=4154
                                           Worker 0: actual time=0.016..0.200 rows=42 loops=1
                                             Buffers: shared hit=182
                                           Worker 1: actual time=0.016..0.228 rows=48 loops=1
                                             Buffers: shared hit=208
                                           ->  Parallel Index Scan Backward using bo_instance_pkey on public.bo_instance i  (cost=0.43..536630.17 rows=504685 width=66) (actual time=0.883..0.953 rows=80 loops=3)
                                                 Output: i.id, i.search_text, i.bo_class_id, i.ts_created, i.ts_updated, i.updated_by_user, i.updated_by_process, i.name, i.is_deleted
                                                 Filter: ((NOT i.is_deleted) AND (i.bo_class_id = 34))
                                                 Rows Removed by Filter: 1155
                                                 Buffers: shared hit=3192
                                                 Worker 0: actual time=0.007..0.037 rows=42 loops=1
                                                   Buffers: shared hit=13
                                                 Worker 1: actual time=0.007..0.042 rows=48 loops=1
                                                   Buffers: shared hit=15
                                           ->  Index Scan using idx_attrib_touchstone_bo_instance_id on public.attrib_touchstone attr  (cost=0.43..1.38 rows=1 width=1521) (actual time=0.005..0.006 rows=1 loops=240)
                                                 Output: attr.id, attr.ts_created, attr.ts_updated, attr.updated_by_user, attr.updated_by_process, attr.bo_instance_id, attr.bo_class_id, attr.num_ports, attr.r_ref, attr.frequency, attr.frequency_hz, attr.data, attr.data_norm
                                                 Index Cond: (i.id = attr.bo_instance_id)
                                                 Buffers: shared hit=962
                                                 Worker 0: actual time=0.002..0.002 rows=1 loops=42
                                                   Buffers: shared hit=169
                                                 Worker 1: actual time=0.002..0.002 rows=1 loops=48
                                                   Buffers: shared hit=193
                               ->  Sort  (cost=45.98..46.43 rows=180 width=40) (actual time=1.664..1.665 rows=1 loops=1)
                                     Output: t.tags_array, t.instance_id
                                     Sort Key: t.instance_id DESC
                                     Sort Method: quicksort  Memory: 41kB
                                     Buffers: shared hit=7
                                     ->  Subquery Scan on t  (cost=32.18..39.24 rows=180 width=40) (actual time=0.735..1.577 rows=191 loops=1)
                                           Output: t.tags_array, t.instance_id
                                           Buffers: shared hit=7
                                           ->  GroupAggregate  (cost=32.18..37.44 rows=180 width=40) (actual time=0.733..1.489 rows=191 loops=1)
                                                 Output: x_boi_tag.bo_instance_id, json_agg(tag.name ORDER BY tag.name)
                                                 Group Key: x_boi_tag.bo_instance_id
                                                 Buffers: shared hit=7
                                                 ->  Sort  (cost=32.18..33.18 rows=401 width=14) (actual time=0.715..0.814 rows=462 loops=1)
                                                       Output: x_boi_tag.bo_instance_id, tag.name
                                                       Sort Key: x_boi_tag.bo_instance_id
                                                       Sort Method: quicksort  Memory: 51kB
                                                       Buffers: shared hit=7
                                                       ->  Hash Join  (cost=4.19..14.84 rows=401 width=14) (actual time=0.111..0.521 rows=462 loops=1)
                                                             Output: x_boi_tag.bo_instance_id, tag.name
                                                             Inner Unique: true
                                                             Hash Cond: (x_boi_tag.tag_id = tag.id)
                                                             Buffers: shared hit=7
                                                             ->  Seq Scan on public.x_boi_tag  (cost=0.00..9.44 rows=444 width=16) (actual time=0.009..0.144 rows=462 loops=1)
                                                                   Output: x_boi_tag.id, x_boi_tag.bo_instance_id, x_boi_tag.tag_id, x_boi_tag.ts_created, x_boi_tag.ts_updated, x_boi_tag.updated_by_user, x_boi_tag.updated_by_process
                                                                   Buffers: shared hit=5
                                                             ->  Hash  (cost=3.03..3.03 rows=93 width=14) (actual time=0.094..0.095 rows=82 loops=1)
                                                                   Output: tag.name, tag.id
                                                                   Buckets: 1024  Batches: 1  Memory Usage: 13kB
                                                                   Buffers: shared hit=2
                                                                   ->  Seq Scan on public.tag  (cost=0.00..3.03 rows=93 width=14) (actual time=0.005..0.059 rows=82 loops=1)
                                                                         Output: tag.name, tag.id
                                                                         Filter: (NOT tag.is_deleted)
                                                                         Rows Removed by Filter: 22
                                                                         Buffers: shared hit=2
                         ->  Sort  (cost=868443.28..871232.88 rows=1115840 width=40) (actual time=25575.835..25575.910 rows=156 loops=1)
                               Output: val.value_list, val.bo_instance_id
                               Sort Key: val.bo_instance_id DESC
                               Sort Method: external merge  Disk: 172768kB
                               Buffers: shared hit=33903, temp read=41731 written=63158
                               ->  Subquery Scan on val  (cost=633959.56..695332.83 rows=1115840 width=40) (actual time=10203.379..23976.330 rows=1209626 loops=1)
                                     Output: val.value_list, val.bo_instance_id
                                     Buffers: shared hit=33903, temp read=14082 written=14082
                                     ->  GroupAggregate  (cost=633959.56..684174.43 rows=1115840 width=40) (actual time=10203.378..23208.688 rows=1209626 loops=1)
                                           Output: x.bo_instance_id, json_agg(json_build_object('name', boattr.name, 'value', v.name))
                                           Group Key: x.bo_instance_id
                                           Buffers: shared hit=33903, temp read=14082 written=14082
                                           ->  Sort  (cost=633959.56..643026.28 rows=3626687 width=23) (actual time=10203.356..11984.566 rows=3626608 loops=1)
                                                 Output: x.bo_instance_id, boattr.name, v.name
                                                 Sort Key: x.bo_instance_id
                                                 Sort Method: external merge  Disk: 112656kB
                                                 Buffers: shared hit=33903, temp read=14082 written=14082
                                                 ->  Hash Left Join  (cost=11.09..90071.01 rows=3626687 width=23) (actual time=0.181..6299.137 rows=3626608 loops=1)
                                                       Output: x.bo_instance_id, boattr.name, v.name
                                                       Inner Unique: true
                                                       Hash Cond: (v.bo_class_attribute_id = boattr.id)
                                                       Buffers: shared hit=33903
                                                       ->  Hash Left Join  (cost=3.51..80289.63 rows=3626687 width=22) (actual time=0.073..3898.956 rows=3626608 loops=1)
                                                             Output: x.bo_instance_id, v.name, v.bo_class_attribute_id
                                                             Inner Unique: true
                                                             Hash Cond: (x.bo_attribute_value_id = v.id)
                                                             Buffers: shared hit=33899
                                                             ->  Seq Scan on public.x_ia_value_list x  (cost=0.00..70163.87 rows=3626687 width=16) (actual time=0.006..1242.323 rows=3626608 loops=1)
                                                                   Output: x.id, x.bo_attribute_value_id, x.bo_instance_id, x.ts_created, x.ts_updated, x.updated_by_user, x.updated_by_process
                                                                   Buffers: shared hit=33897
                                                             ->  Hash  (cost=2.67..2.67 rows=67 width=22) (actual time=0.060..0.061 rows=83 loops=1)
                                                                   Output: v.name, v.id, v.bo_class_attribute_id
                                                                   Buckets: 1024  Batches: 1  Memory Usage: 13kB
                                                                   Buffers: shared hit=2
                                                                   ->  Seq Scan on public.bo_attribute_value v  (cost=0.00..2.67 rows=67 width=22) (actual time=0.003..0.031 rows=83 loops=1)
                                                                         Output: v.name, v.id, v.bo_class_attribute_id
                                                                         Buffers: shared hit=2
                                                       ->  Hash  (cost=5.59..5.59 rows=159 width=17) (actual time=0.103..0.103 rows=159 loops=1)
                                                             Output: boattr.name, boattr.id
                                                             Buckets: 1024  Batches: 1  Memory Usage: 17kB
                                                             Buffers: shared hit=4
                                                             ->  Seq Scan on public.bo_class_attribute boattr  (cost=0.00..5.59 rows=159 width=17) (actual time=0.003..0.050 rows=159 loops=1)
                                                                   Output: boattr.name, boattr.id
                                                                   Buffers: shared hit=4
                   ->  Aggregate  (cost=1.51..1.52 rows=1 width=32) (actual time=0.010..0.010 rows=1 loops=150)
                         Output: json_agg((json_array_elements.value #>> '{value}'::text[]) ORDER BY (json_array_elements.value #>> '{value}'::text[]))
                         ->  Function Scan on pg_catalog.json_array_elements  (cost=0.00..1.50 rows=1 width=32) (actual time=0.005..0.006 rows=1 loops=150)
                               Output: json_array_elements.value
                               Function Call: json_array_elements(val.value_list)
                               Filter: ((json_array_elements.value #>> '{name}'::text[]) = 'freq_units'::text)
                               Rows Removed by Filter: 2
             ->  Aggregate  (cost=1.51..1.52 rows=1 width=32) (actual time=0.008..0.008 rows=1 loops=150)
                   Output: json_agg((json_array_elements_1.value #>> '{value}'::text[]) ORDER BY (json_array_elements_1.value #>> '{value}'::text[]))
                   ->  Function Scan on pg_catalog.json_array_elements json_array_elements_1  (cost=0.00..1.50 rows=1 width=32) (actual time=0.004..0.004 rows=1 loops=150)
                         Output: json_array_elements_1.value
                         Function Call: json_array_elements(val.value_list)
                         Filter: ((json_array_elements_1.value #>> '{name}'::text[]) = 'parameter_type'::text)
                         Rows Removed by Filter: 2
       ->  Aggregate  (cost=1.51..1.52 rows=1 width=32) (actual time=0.040..0.040 rows=1 loops=150)
             Output: json_agg((json_array_elements_2.value #>> '{value}'::text[]) ORDER BY (json_array_elements_2.value #>> '{value}'::text[]))
             ->  Function Scan on pg_catalog.json_array_elements json_array_elements_2  (cost=0.00..1.50 rows=1 width=32) (actual time=0.003..0.004 rows=1 loops=150)
                   Output: json_array_elements_2.value
                   Function Call: json_array_elements(val.value_list)
                   Filter: ((json_array_elements_2.value #>> '{name}'::text[]) = 'format'::text)
                   Rows Removed by Filter: 2
Planning time: 1.027 ms
Execution time: 25728.499 ms

我對查詢優化不太熟悉。歡迎任何建議。至少你能指出我必須在哪裡創建額外的索引嗎?先感謝您。

您花費大量時間打包子查詢“x”,只是為了從中提取 150 個單值結果。您可以將“x”上的子查詢移出連接並進入選擇列表嗎?或者,也許,進入橫向連接?這樣,它就可以提取並打包要返回的特定行的數據。

如果沒有針對所有表、索引和約束的 CREATE 語句,就很難提出一個確切的語法供您使用,因為我沒有簡單的方法來測試它。

此外,所有這些左連接真的有必要嗎?如果你不需要對所有隱含的 NULL 做任何事情,也許你可以去掉一些’left’。

我會說問題是對索引掃描的嚴重錯誤估計public.bo_instance

->  Parallel Index Scan Backward using bo_instance_pkey on public.bo_instance i
               (cost=0.43..536630.17 rows=504685 width=66)
               (actual time=0.883..0.953 rows=80 loops=3)
     Filter: ((NOT i.is_deleted) AND (i.bo_class_id = 34))
     Rows Removed by Filter: 1155
     Buffers: shared hit=3192

統計數據似乎遙遙無期。桌子看起來也很臃腫;我會先跑

VACUUM (FULL) public.bo_instance;

進而

VACUUM (ANALYZE) public.bo_instance;

解決這個問題。

如果 PostgreSQL 得到正確的估計,它可能會選擇嵌套循環連接並且速度更快。

引用自:https://dba.stackexchange.com/questions/237537