編集:同じデータでテストするためのDockerイメージ(難読化)
$ docker run --rm --name pg -d homer5439/pgagg
$ docker exec -ti pg bash
# createdb -U postgres test; zcat /tmp/corr.sql.gz | psql -U postgres test
# psql -U postgres test
以下の例を実行できます。
Postgresql 12.2で次のクエリが30秒以上かかるのはなぜでしょうか。
SELECT
contract_id,
array_agg(corr) AS corr
FROM
corr
GROUP BY contract_id;
詳細:corr
テーブルには約150000レコードがあります。各レコードの構造は次のとおりです。
gse=# \d corr ;
Table "public.corr"
Column | Type | Collation | Nullable | Default
-------------+---------+-----------+----------+-----------------------------------------------------------------
corr_id | integer | | not null | nextval('corr_corr_id_seq'::regclass)
contract_id | integer | | |
start_date | date | | |
corr_type | text | | |
descr | text | | |
credit | numeric | | |
detail | text | | |
Indexes:
"corr_pkey" PRIMARY KEY, btree (corr_id)
"corr_contract_id_idx" btree (contract_id)
detail
フィールドには、最大2/3 MBの長さのテキストが含まれます(レコードの約10%にその詳細が含まれ、その他のレコードには(〜10-20)キロバイトがあります)。個別のcontract_id
値の数は現在2317です。
このサイトで見つかった他の提案に従って、目立った変更なしにwork_mem
の値を最大10GBに変更してみました。
クエリプランは次のとおりです。
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------------------
HashAggregate (cost=9883.29..9911.57 rows=2263 width=36) (actual time=1184.971..1357.309 rows=2317 loops=1)
Output: contract_id, array_agg(corr.*)
Group Key: corr.contract_id
Buffers: shared hit=78012 read=49899
-> Seq Scan on public.corr (cost=0.00..9320.19 rows=112619 width=571 (actual time=0.057..959.359 rows=112619 loops=1)
Output: contract_id, corr.*
Buffers: shared hit=78012 read=49899
Planning Time: 0.131 ms
Execution Time: 1357.747 ms
「スタンドアロン」を実行する場合、「psql」で実行することを意味しますが、EXPLAIN ANALYZEを使用しないと、「psql」は結果セット全体をメモリに読み取り、そのセットを調べて、それぞれの最長エントリの長さを決定します。列をその長さにフォーマットできるようにします。 2317行の場合30秒は期待できませんが、遅くなる可能性があります。
「corr」テーブルに「corr」列がないことを除いて、集計しているのは行レコード全体であり、すでに言ったように、非常に幅が広くなる可能性があります。そのため、psqlで大量のデータを投入しているため、処理に時間がかかり、スワップが発生する可能性があります。