#author("2019-12-20T14:36:45+00:00","default:haikikyou","haikikyou")
#author("2019-12-23T11:00:02+00:00","default:haikikyou","haikikyou")
[[PostgreSQL/調査検証]]
#contents
* パーティション性能 [#f665c62f]
PostgreSQLでは、バージョン10から宣言的パーティションが使えるようになっている。~
10以前はトリガを使用することで、別々のテーブルにアクセスする必要があった。~
またPostgreSQL11から12に変わり、パーティション性能の向上が図られた。~
ここでは、簡単なアプリケーションを使ってパーティション性能について測定した結果を参考までに記載する。~
(細かい設定やチューニングまでは考慮が及んでないので、その点については事前情報としてインプットください)
* 測定環境 [#u4794023]
個人用のデスクトップ環境で以下の通り。
|~項目|~スペック|h
|OS macOS High Sierra|10.13.6(17G9016)|
|Model|iMac (27-inch, Late 2012)|
|CPU|3.2 GHz Intel Core i5|
|メモリ|32 GB 1600 MHz DDR3|
|グラフィックス|NVIDIA GeForce GTX 680MX 2048 MB|
* 測定条件 [#u27250eb]
+ PostgreSQL 11.6 / 12.1
+ パーティション / パーティション子テーブルアクセス / 非パーティション
+ シングルプロセス / マルチプロセス
+ パーティション数 10 / 100 / 1000 / 2000
-- パーティションはRANGE
-- デフォルトパーティションは無し
実行内容は以下の通り。
- 1パーティションで1000件のレコードを入れる。
- 10パーティションのレコード(10 x 1000件 = 10000件)のレコードをSELECT...FOR UPDATE、UPDATEする。
- 複数プロセス(5プロセス)の場合は、各プロセスが2パーティションを実行するよう配分する。
* 測定用資材 [#n8223f22]
測定に使ったアプリケーション一式は、以下の通り。~
測定のために簡易的に自作したもの。
** アプリケーション [#oec28e2d]
psycopg2のインストールが必要である。
- &ref(bench.py);
#geshi{{{
$ pip install psycopg2
}}}
**セットアップ&実行スクリプト [#mc2d153c]
- &ref(partition_test.sh);
- &ref(run_bench_test_suite.sh);
- &ref(run_bench_test_suite_multiproc.sh);
&label(sample){例}; ''セットアップ例''
#geshi(bash){{{
# 非パーティションセットアップ
partition_test.sh setup
# パーティションセットアップ
partition_test.sh setup --part
# パーティション(インデックス有り)セットアップ
partition_test.sh setup --part --index
# 非パーティション/パーティション、インデックス有りセットアップ
partition_test.sh setup --all --index
}}}
** PostgreSQLのインストール [#la2600b9]
拙作であるがpgenv2を使うと楽に入る。
#geshi{{{
$ pgenv install 11.6
$ pgenv install 12.1
}}}
&label(link){URL};https://github.com/moritetu/pgenv2/graphs/traffic
&label(link){URL};https://github.com/moritetu/pgenv2
* 結果 [#p0b743d7]
- PostgreSQL11では、パーティション数が10、100と上がるにつれて、大幅に性能が落ちる。~
ただし、子テーブルに直接アクセスすれば、PostgreSQL12ともほとんど変わらない。
- 一方、PostgreSQL12では、パーティション数の増加につれほとんど性能の劣化がない。~
2000パーティションまで上げたが、パーティション数10の時と比較してもほとんど処理時間は変わらなかった。
** 1プロセス [#aed64c6c]
- 1プロセスで、10テーブル、テーブルあたり1000件更新(10000件更新)
- PostgreSQL11の1000パーティション以降は、測定マシン環境ではアプリが長時間経っても終わらず測定断念
#ref(./partition-1proc.png)
|~Case|~10 part|~100 part|~1000 part|~2000 part|h
|PG11 - Part|13.8981840610504|62.0137369632721|測定不可|測定不可|
|PG11 - PartChild|8.63194012641907|8.98710894584656|8.24935483932495|8.13933205604553|
|PG11 - NoPart|8.66525411605835|8.3603720664978|8.07384300231934|8.25173592567444|
|PG12 - Part|9.50577211380005|9.65898609161377|9.86613392829895|9.74069404602051|
|PG12 - PartChild|8.57707095146179|9.53609609603882|8.34612584114075|8.33119201660156|
|PG12 - NoPart|8.41914796829224|8.76705479621887|8.47983002662659|8.42714786529541|
(seconds)
** 5プロセス [#c7d17821]
- 5プロセスで、10テーブル、テーブルあたり1000件更新(10000件更新)
- 5プロセスは、それぞれパーティションレンジの異なるテーブルにアクセス
- PostgreSQL11の1000パーティション以降は、測定マシン環境で長時間終わらず測定断念
#ref(./partition-5proc.png)
|~Case|~10 part|~100 part|~1000 part|~2000 part|h
|PG11 - Part|3.62266898155212|19.0739562034607|測定不可|測定不可|
|PG11 - PartChild|2.36356763839722|2.80864982604981|2.49761853218079|2.72020430564881|
|PG11 - NoPart|2.181214427948|2.61257362365723|2.56392440795898|2.30714211463928|
|PG12 - Part|2.43653764724731|2.58718962669373|2.47538347244263|2.74749002456665|
|PG12 - PartChild|2.18321080207825|2.24497199058533|2.28030920028687|2.33017301559448|
|PG12 - NoPart|2.31623935699463|2.31141901016235|2.44624223709107|2.23168058395386|
(seconds)
* PostgreSQL11と12でのパーティションのロック [#c4cde79f]
** PostgreSQL11 [#h4e5e85e]
PostgreSQL11では、親テーブルのロックを得ると、データレンジにない子テーブルのロックも獲得される。
#geshi(sql){{{
$ ./partition_test.sh --part 1000 20
$ psql -d postgres
psql (11.6)
Type "help" for help.
postgres=# \d+ part_*;
Table "public.part_tbl"
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
--------+---------+-----------+----------+---------+----------+--------------+-------------
id | integer | | | | plain | |
msg | text | | | | extended | |
Partition key: RANGE (id)
Partitions: part_tbl_1 FOR VALUES FROM (1) TO (1001),
part_tbl_10 FOR VALUES FROM (9001) TO (10001),
part_tbl_11 FOR VALUES FROM (10001) TO (11001),
part_tbl_12 FOR VALUES FROM (11001) TO (12001),
part_tbl_13 FOR VALUES FROM (12001) TO (13001),
part_tbl_14 FOR VALUES FROM (13001) TO (14001),
part_tbl_15 FOR VALUES FROM (14001) TO (15001),
part_tbl_16 FOR VALUES FROM (15001) TO (16001),
part_tbl_17 FOR VALUES FROM (16001) TO (17001),
part_tbl_18 FOR VALUES FROM (17001) TO (18001),
part_tbl_19 FOR VALUES FROM (18001) TO (19001),
part_tbl_2 FOR VALUES FROM (1001) TO (2001),
part_tbl_20 FOR VALUES FROM (19001) TO (20001),
part_tbl_3 FOR VALUES FROM (2001) TO (3001),
part_tbl_4 FOR VALUES FROM (3001) TO (4001),
part_tbl_5 FOR VALUES FROM (4001) TO (5001),
part_tbl_6 FOR VALUES FROM (5001) TO (6001),
part_tbl_7 FOR VALUES FROM (6001) TO (7001),
part_tbl_8 FOR VALUES FROM (7001) TO (8001),
part_tbl_9 FOR VALUES FROM (8001) TO (9001)
Table "public.part_tbl_1"
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
--------+---------+-----------+----------+---------+----------+--------------+-------------
id | integer | | | | plain | |
msg | text | | | | extended | |
Partition of: part_tbl FOR VALUES FROM (1) TO (1001)
Partition constraint: ((id IS NOT NULL) AND (id >= 1) AND (id < 1001))
-- ページ節約のため、残りのテーブル情報については省略
postgres=# begin;
BEGIN
postgres=# select id from part_tbl where id = 1;
id
----
1
(1 row)
postgres=# update part_tbl set msg = 'hoge' where id = 1;
UPDATE 1
}}}
#geshi(sql){{{
-- select実行後にpg_locksを参照
postgres=# select pl.pid, pc.relname, pl.mode from pg_locks pl inner join pg_class pc on (pl.relation = pc.oid) where pl.pid = 83066;
pid | relname | mode
-------+-------------+-----------------
83066 | part_tbl | AccessShareLock
83066 | part_tbl_17 | AccessShareLock
83066 | part_tbl_5 | AccessShareLock
83066 | part_tbl_18 | AccessShareLock
83066 | part_tbl_9 | AccessShareLock
83066 | part_tbl_13 | AccessShareLock
83066 | part_tbl_15 | AccessShareLock
83066 | part_tbl_16 | AccessShareLock
83066 | part_tbl_20 | AccessShareLock
83066 | part_tbl_1 | AccessShareLock
83066 | part_tbl_10 | AccessShareLock
83066 | part_tbl_2 | AccessShareLock
83066 | part_tbl_6 | AccessShareLock
83066 | part_tbl_3 | AccessShareLock
83066 | part_tbl_7 | AccessShareLock
83066 | part_tbl_11 | AccessShareLock
83066 | part_tbl_14 | AccessShareLock
83066 | part_tbl_4 | AccessShareLock
83066 | part_tbl_8 | AccessShareLock
83066 | part_tbl_12 | AccessShareLock
83066 | part_tbl_19 | AccessShareLock
(21 rows)
-- update実行後に再度pg_locksを参照
postgres=# select pl.pid, pc.relname, pl.mode from pg_locks pl inner join pg_class pc on (pl.relation = pc.oid) where pl.pid = 83066;
pid | relname | mode
-------+-------------+------------------
83066 | part_tbl | RowExclusiveLock
83066 | part_tbl | AccessShareLock
83066 | part_tbl_17 | RowExclusiveLock
83066 | part_tbl_17 | AccessShareLock
83066 | part_tbl_5 | RowExclusiveLock
83066 | part_tbl_5 | AccessShareLock
83066 | part_tbl_18 | RowExclusiveLock
83066 | part_tbl_18 | AccessShareLock
83066 | part_tbl_9 | RowExclusiveLock
83066 | part_tbl_9 | AccessShareLock
83066 | part_tbl_13 | RowExclusiveLock
83066 | part_tbl_13 | AccessShareLock
83066 | part_tbl_15 | RowExclusiveLock
83066 | part_tbl_15 | AccessShareLock
83066 | part_tbl_16 | RowExclusiveLock
83066 | part_tbl_16 | AccessShareLock
83066 | part_tbl_20 | RowExclusiveLock
83066 | part_tbl_20 | AccessShareLock
83066 | part_tbl_1 | RowExclusiveLock
83066 | part_tbl_1 | AccessShareLock
83066 | part_tbl_10 | RowExclusiveLock
83066 | part_tbl_10 | AccessShareLock
83066 | part_tbl_2 | RowExclusiveLock
83066 | part_tbl_2 | AccessShareLock
83066 | part_tbl_6 | RowExclusiveLock
83066 | part_tbl_6 | AccessShareLock
83066 | part_tbl_3 | RowExclusiveLock
83066 | part_tbl_3 | AccessShareLock
83066 | part_tbl_7 | RowExclusiveLock
83066 | part_tbl_7 | AccessShareLock
83066 | part_tbl_11 | RowExclusiveLock
83066 | part_tbl_11 | AccessShareLock
83066 | part_tbl_14 | RowExclusiveLock
83066 | part_tbl_14 | AccessShareLock
83066 | part_tbl_4 | RowExclusiveLock
83066 | part_tbl_4 | AccessShareLock
83066 | part_tbl_8 | RowExclusiveLock
83066 | part_tbl_8 | AccessShareLock
83066 | part_tbl_12 | RowExclusiveLock
83066 | part_tbl_12 | AccessShareLock
83066 | part_tbl_19 | RowExclusiveLock
83066 | part_tbl_19 | AccessShareLock
(42 rows)
}}}
** PostgreSQL12 [#fe0f57e7]
*** 親テーブルアクセス [#cc7b7b8b]
PostgreSQL12では、親テーブル経由でアクセスしても、対象の子テーブルのみのロックが取られている。
''SELECT .. ''
#geshi(sql){{{
$ psql -d postgres
psql (12.1)
Type "help" for help.
postgres=# begin;
BEGIN
postgres=# select id from part_tbl where id = 1;
id
----
1
(1 row)
postgres=# update part_tbl set msg = 'hoge' where id = 1;
UPDATE 1
}}}
#geshi(sql){{{
postgres=# select pl.pid, pc.relname, pl.mode from pg_locks pl inner join pg_class pc on (pl.relation = pc.oid) where pl.pid = 86768;
pid | relname | mode
-------+------------+-----------------
86768 | part_tbl | AccessShareLock
86768 | part_tbl_1 | AccessShareLock
(2 rows)
postgres=# select pl.pid, pc.relname, pl.mode from pg_locks pl inner join pg_class pc on (pl.relation = pc.oid) where pl.pid = 86768;
pid | relname | mode
-------+------------+------------------
86768 | part_tbl | RowExclusiveLock
86768 | part_tbl | AccessShareLock
86768 | part_tbl_1 | RowExclusiveLock
86768 | part_tbl_1 | AccessShareLock
(4 rows)
}}}
''SELECT .. FOR UPDATE''
#geshi(sql){{{
$ psql -d postgres
psql (12.1)
Type "help" for help.
postgres=# begin;
BEGIN
postgres=# SELECT id FROM part_tbl WHERE id = 1 for update;
id
----
1
(1 行)
postgres=# UPDATE part_tbl SET msg = 'hoge' WHERE id = 1;
UPDATE 1
}}}
#geshi(sql){{{
postgres=# SELECT pl.pid, pc.relname, pl.mode FROM pg_locks pl INNER JOIN pg_class pc ON (pl.relation = pc.oid) WHERE pl.pid = 60079;
pid | relname | mode
-------+------------+--------------
60079 | part_tbl | RowShareLock
60079 | part_tbl_1 | RowShareLock
(2 行)
postgres=# SELECT pl.pid, pc.relname, pl.mode FROM pg_locks pl INNER JOIN pg_class pc ON (pl.relation = pc.oid) WHERE pl.pid = 60079;
pid | relname | mode
-------+------------+------------------
60079 | part_tbl | RowExclusiveLock
60079 | part_tbl | RowShareLock
60079 | part_tbl_1 | RowExclusiveLock
60079 | part_tbl_1 | RowShareLock
(4 行)
}}}
*** 子テーブルアクセス [#r4859264]
''SELECT .. ''
#geshi(sql){{{
$ psql -d postgres
psql (12.1)
Type "help" for help.
postgres=# begin;
BEGIN
postgres=# select id from part_tbl_1 where id = 1;
id
----
1
(1 row)
postgres=# update part_tbl_1 set msg = 'hoge' where id = 1;
UPDATE 1
}}}
#geshi(sql){{{
postgres=# SELECT pl.pid, pc.relname, pl.mode FROM pg_locks pl INNER JOIN pg_class pc ON (pl.relation = pc.oid) WHERE pl.pid = 60079;
pid | relname | mode
-------+------------+-----------------
60079 | part_tbl_1 | AccessShareLock
(1 行)
postgres=# SELECT pl.pid, pc.relname, pl.mode FROM pg_locks pl INNER JOIN pg_class pc ON (pl.relation = pc.oid) WHERE pl.pid = 60079;
pid | relname | mode
-------+------------+------------------
60079 | part_tbl | AccessShareLock
60079 | part_tbl_1 | RowExclusiveLock
60079 | part_tbl_1 | AccessShareLock
(3 行)
}}}
''SELECT .. FOR UPDATE''
#geshi(sql){{{
$ psql -d postgres
psql (12.1)
Type "help" for help.
postgres=# begin;
BEGIN
postgres=# SELECT id FROM part_tbl_1 WHERE id = 1 for update;
id
----
1
(1 行)
postgres=# UPDATE part_tbl_1 SET msg = 'hoge' WHERE id = 1;
UPDATE 1
}}}
#geshi(sql){{{
postgres=# SELECT pl.pid, pc.relname, pl.mode FROM pg_locks pl INNER JOIN pg_class pc ON (pl.relation = pc.oid) WHERE pl.pid = 60079;
pid | relname | mode
-------+------------+--------------
60079 | part_tbl_1 | RowShareLock
(1 行)
postgres=# SELECT pl.pid, pc.relname, pl.mode FROM pg_locks pl INNER JOIN pg_class pc ON (pl.relation = pc.oid) WHERE pl.pid = 60079;
pid | relname | mode
-------+------------+------------------
60079 | part_tbl_1 | RowExclusiveLock
60079 | part_tbl_1 | RowShareLock
(2 行)
}}}
*** 子テーブルアクセス+親テーブルアクセス [#jdf0c90b]
#geshi(sql){{{
$ psql -d postgres
psql (12.1)
Type "help" for help.
postgres=# begin;
BEGIN
postgres=# SELECT id FROM part_tbl_1 WHERE id = 1;
id
----
1
(1 行)
postgres=# UPDATE part_tbl SET msg = 'hoge' WHERE id = 1;
UPDATE 1
}}}
#geshi(sql){{{
postgres=# SELECT pl.pid, pc.relname, pl.mode FROM pg_locks pl INNER JOIN pg_class pc ON (pl.relation = pc.oid) WHERE pl.pid = 60079;
pid | relname | mode
-------+------------+-----------------
60079 | part_tbl_1 | AccessShareLock
(1 行)
postgres=# SELECT pl.pid, pc.relname, pl.mode FROM pg_locks pl INNER JOIN pg_class pc ON (pl.relation = pc.oid) WHERE pl.pid = 60079;
pid | relname | mode
-------+------------+------------------
60079 | part_tbl | RowExclusiveLock
60079 | part_tbl_1 | RowExclusiveLock
60079 | part_tbl_1 | AccessShareLock
(3 行)
}}}
*** 親テーブルアクセス+子テーブルアクセス [#w363d4cc]
#geshi(sql){{{
$ psql -d postgres
psql (12.1)
Type "help" for help.
postgres=# begin;
BEGIN
postgres=# SELECT id FROM part_tbl WHERE id = 1;
id
----
1
(1 行)
postgres=# UPDATE part_tbl_1 SET msg = 'hoge' WHERE id = 1;
UPDATE 1
}}}
#geshi(sql){{{
postgres=# SELECT pl.pid, pc.relname, pl.mode FROM pg_locks pl INNER JOIN pg_class pc ON (pl.relation = pc.oid) WHERE pl.pid = 60079;
pid | relname | mode
-------+------------+-----------------
60079 | part_tbl | AccessShareLock
60079 | part_tbl_1 | AccessShareLock
(2 行)
postgres=# SELECT pl.pid, pc.relname, pl.mode FROM pg_locks pl INNER JOIN pg_class pc ON (pl.relation = pc.oid) WHERE pl.pid = 60079;
pid | relname | mode
-------+------------+------------------
60079 | part_tbl | AccessShareLock
60079 | part_tbl_1 | RowExclusiveLock
60079 | part_tbl_1 | AccessShareLock
(3 行)
}}}
* ソースコードの調査 [#k2f3b658]
** プランニング時のロック [#r9b41ee9]
今回実行したクエリについての調査した内容である。
*** パーティションテーブルに関する情報の抽出 [#ta416aea]
パーティションテーブルについては、以下で抽出されている。
&label(warn){参考}; [[build_simple_rel()>https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/optimizer/util/relnode.c;hb=be9d4b9280606a0b0984ba46c452a48961cf92d4#l182]] - &size(11){&color(gray){https://git.postgresql.org/gitweb/?p=postgresql.git;a=tree;h=refs/heads/REL_12_STABLE};};
#geshi(c){{{
287 /* Check type of rtable entry */
288 switch (rte->rtekind)
289 {
290 case RTE_RELATION:
291 /* Table --- retrieve statistics from the system catalogs */
292 get_relation_info(root, rte->relid, rte->inh, rel);
293 break;
}}}
スタックトレースは以下の通り。
#geshi{{{
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = step in
* frame #0: 0x0000000103d98261 postgres`build_simple_rel(root=0x00007fb012033440, relid=1, parent=0x0000000000000000) at relnode.c:293
frame #1: 0x0000000103d53a2a postgres`add_base_rels_to_query(root=0x00007fb012033440, jtnode=0x00007fb011823818) at initsplan.c:114
frame #2: 0x0000000103d53a7a postgres`add_base_rels_to_query(root=0x00007fb012033440, jtnode=0x00007fb011823b60) at initsplan.c:122
frame #3: 0x0000000103d589ee postgres`query_planner(root=0x00007fb012033440, qp_callback=(postgres`standard_qp_callback at planner.c:3589), qp_extra=0x00007ffeec2a5948) at planmain.c:168
frame #4: 0x0000000103d5c8c8 postgres`grouping_planner(root=0x00007fb012033440, inheritance_update=false, tuple_fraction=0) at planner.c:2048
frame #5: 0x0000000103d5a298 postgres`subquery_planner(glob=0x00007fb011823d28, parse=0x00007fb011823388, parent_root=0x0000000000000000, hasRecursion=false, tuple_fraction=0) at planner.c:1012
frame #6: 0x0000000103d58e5b postgres`standard_planner(parse=0x00007fb011823388, cursorOptions=256, boundParams=0x0000000000000000) at planner.c:406
frame #7: 0x0000000103d58bee postgres`planner(parse=0x00007fb011823388, cursorOptions=256, boundParams=0x0000000000000000) at planner.c:275
frame #8: 0x0000000103e92879 postgres`pg_plan_query(querytree=0x00007fb011823388, cursorOptions=256, boundParams=0x0000000000000000) at postgres.c:878
frame #9: 0x0000000103e929e1 postgres`pg_plan_queries(querytrees=0x00007fb011823cf0, cursorOptions=256, boundParams=0x0000000000000000) at postgres.c:968
frame #10: 0x0000000103e957e1 postgres`exec_simple_query(query_string="SELECT id FROM part_tbl WHERE id = 1 FOR UPDATE;") at postgres.c:1143
frame #11: 0x0000000103e94b68 postgres`PostgresMain(argc=1, argv=0x00007fb011844d58, dbname="postgres", username="t-moriyasu") at postgres.c:4236
frame #12: 0x0000000103dca700 postgres`BackendRun(port=0x00007fb0116007c0) at postmaster.c:4437
frame #13: 0x0000000103dc9aea postgres`BackendStartup(port=0x00007fb0116007c0) at postmaster.c:4128
frame #14: 0x0000000103dc8a3a postgres`ServerLoop at postmaster.c:1704
frame #15: 0x0000000103dc626f postgres`PostmasterMain(argc=1, argv=0x00007fb011407260) at postmaster.c:1377
frame #16: 0x0000000103cc6149 postgres`main(argc=1, argv=0x00007fb011407260) at main.c:228
frame #17: 0x00007fff73b1b015 libdyld.dylib`start + 1
frame #18: 0x00007fff73b1b015 libdyld.dylib`start + 1
(lldb)
}}}
*** パーティション子テーブルに関するロック [#h7e58846]
add_other_rels_to_query(root)で、パーティションの子テーブルもスキャン対象となり、ロックも獲得される。~
子テーブルのロックモードは親テーブルと同じである。
&label(warn){参考}; [[add_other_rels_to_query()>https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/optimizer/plan/initsplan.c;hb=be9d4b9280606a0b0984ba46c452a48961cf92d4#l144]] - &size(11){&color(gray){https://git.postgresql.org/gitweb/?p=postgresql.git;a=tree;h=refs/heads/REL_12_STABLE};};
#geshi{{{
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 47.1
* frame #0: 0x0000000103e6ec75 postgres`LockAcquireExtended(locktag=0x00007ffeec2a54b8, lockmode=2, sessionLock=false, dontWait=false, reportMemoryError=true, locallockp=0x00007ffeec2a54b0) at lock.c:890
frame #1: 0x0000000103e6bf30 postgres`LockRelationOid(relid=16387, lockmode=2) at lmgr.c:116
frame #2: 0x000000010396fddb postgres`relation_open(relationId=16387, lockmode=2) at relation.c:56
frame #3: 0x0000000103a4b449 postgres`table_open(relationId=16387, lockmode=2) at table.c:43
frame #4: 0x0000000103d84ac7 postgres`expand_partitioned_rtentry(root=0x00007fb013000840, relinfo=0x00007fb011823dc0, parentrte=0x00007fb0118234a0, parentRTindex=1, parentrel=0x000000010490aa88, top_parentrc=0x00007fb013000c80, lockmode=2) at inherit.c:361
frame #5: 0x0000000103d8427a postgres`expand_inherited_rtentry(root=0x00007fb013000840, rel=0x00007fb011823dc0, rte=0x00007fb0118234a0, rti=1) at inherit.c:143
frame #6: 0x0000000103d53bf3 postgres`add_other_rels_to_query(root=0x00007fb013000840) at initsplan.c:163
frame #7: 0x0000000103d58a91 postgres`query_planner(root=0x00007fb013000840, qp_callback=(postgres`standard_qp_callback at planner.c:3589), qp_extra=0x00007ffeec2a5948) at planmain.c:266
frame #8: 0x0000000103d5c8c8 postgres`grouping_planner(root=0x00007fb013000840, inheritance_update=false, tuple_fraction=0) at planner.c:2048
frame #9: 0x0000000103d5a298 postgres`subquery_planner(glob=0x00007fb011823d28, parse=0x00007fb011823388, parent_root=0x0000000000000000, hasRecursion=false, tuple_fraction=0) at planner.c:1012
frame #10: 0x0000000103d58e5b postgres`standard_planner(parse=0x00007fb011823388, cursorOptions=256, boundParams=0x0000000000000000) at planner.c:406
frame #11: 0x0000000103d58bee postgres`planner(parse=0x00007fb011823388, cursorOptions=256, boundParams=0x0000000000000000) at planner.c:275
frame #12: 0x0000000103e92879 postgres`pg_plan_query(querytree=0x00007fb011823388, cursorOptions=256, boundParams=0x0000000000000000) at postgres.c:878
frame #13: 0x0000000103e929e1 postgres`pg_plan_queries(querytrees=0x00007fb011823cf0, cursorOptions=256, boundParams=0x0000000000000000) at postgres.c:968
frame #14: 0x0000000103e957e1 postgres`exec_simple_query(query_string="SELECT id FROM part_tbl WHERE id = 1 FOR UPDATE;") at postgres.c:1143
frame #15: 0x0000000103e94b68 postgres`PostgresMain(argc=1, argv=0x00007fb011844d58, dbname="postgres", username="t-moriyasu") at postgres.c:4236
frame #16: 0x0000000103dca700 postgres`BackendRun(port=0x00007fb0116007c0) at postmaster.c:4437
frame #17: 0x0000000103dc9aea postgres`BackendStartup(port=0x00007fb0116007c0) at postmaster.c:4128
frame #18: 0x0000000103dc8a3a postgres`ServerLoop at postmaster.c:1704
frame #19: 0x0000000103dc626f postgres`PostmasterMain(argc=1, argv=0x00007fb011407260) at postmaster.c:1377
frame #20: 0x0000000103cc6149 postgres`main(argc=1, argv=0x00007fb011407260) at main.c:228
frame #21: 0x00007fff73b1b015 libdyld.dylib`start + 1
frame #22: 0x00007fff73b1b015 libdyld.dylib`start + 1
(lldb)
}}}
パーティションテーブルの場合は継承フラグが立っており、子テーブルの展開が行われる。
&label(warn){参考}; [[add_other_rels_to_query()>https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/optimizer/plan/initsplan.c;hb=be9d4b9280606a0b0984ba46c452a48961cf92d4#l161]] - &size(11){&color(gray){https://git.postgresql.org/gitweb/?p=postgresql.git;a=tree;h=refs/heads/REL_12_STABLE};};
#geshi{{{
161 /* If it's marked as inheritable, look for children. */
162 if (rte->inh)
163 expand_inherited_rtentry(root, rel, rte, rti);
}}}
子テーブルを直接指定すると、上記のexpand_inherited_rtentryは実行されないため、非パーティション同等となる?のでは(おそらく)。
** Partition Pruning [#x9e2042f]
&label(warn){参考};[[prune_append_rel_partitions()>https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/partitioning/partprune.c;hb=be9d4b9280606a0b0984ba46c452a48961cf92d4#l663]] - &size(11){&color(gray){https://git.postgresql.org/gitweb/?p=postgresql.git;a=tree;h=refs/heads/REL_12_STABLE};};
大まかな流れは以下の通り。
+ pruningの戦略の決定
++ パーティション数が0ならば何もしない
++ enable_partition_pruningが無効または句がなければ全てのパーティションが対象
++ pruningステップの構築(gen_partprune_steps)~
どのような戦略でパーティション抽出が行われるかは、パーティションの種別(HASH、LIST、RANGE)やWHERE句の条件で決まる。~
&label(warn){参考}; [[gen_partprune_steps_internal()>https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/partitioning/partprune.c;hb=be9d4b9280606a0b0984ba46c452a48961cf92d4#l857]] - &size(11){&color(gray){https://git.postgresql.org/gitweb/?p=postgresql.git;a=tree;h=refs/heads/REL_12_STABLE};};
#geshi(c){{{
}}}
++ 句が矛盾している場合(例えば WHERE id is null のような場合)は対象なし
++ 使用可能な戦略を構築できなかった場合は全てのパーティションが対象
+ partition pruningでマッチするリレーションを抽出(get_matching_partitions)
++ PartitionPruneStepを順次実行し、複数条件あれば統合して必要なパーティションを抽出する~
&label(warn){参考};[[get_matching_partitions()>https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/partitioning/partprune.c;hb=be9d4b9280606a0b0984ba46c452a48961cf92d4#l729]] - &size(11){&color(gray){https://git.postgresql.org/gitweb/?p=postgresql.git;a=tree;h=refs/heads/REL_12_STABLE};};
#geshi(c){{{
755 foreach(lc, pruning_steps)
756 {
757 PartitionPruneStep *step = lfirst(lc);
758
759 switch (nodeTag(step))
760 {
761 case T_PartitionPruneStepOp:
762 results[step->step_id] =
763 perform_pruning_base_step(context,
764 (PartitionPruneStepOp *) step);
765 break;
766
767 case T_PartitionPruneStepCombine:
768 results[step->step_id] =
769 perform_pruning_combine_step(context,
770 (PartitionPruneStepCombine *) step,
771 results);
772 break;
773
774 default:
775 elog(ERROR, "invalid pruning step type: %d",
776 (int) nodeTag(step));
777 }
778 }
779
780 /*
781 * At this point we know the offsets of all the datums whose corresponding
782 * partitions need to be in the result, including special null-accepting
783 * and default partitions. Collect the actual partition indexes now.
784 */
785 final_result = results[num_steps - 1];
786 Assert(final_result != NULL);
787 i = -1;
788 result = NULL;
789 scan_default = final_result->scan_default;
790 while ((i = bms_next_member(final_result->bound_offsets, i)) >= 0)
791 {
792 int partindex = context->boundinfo->indexes[i];
793
794 if (partindex < 0)
795 {
796 /*
797 * In range partitioning cases, if a partition index is -1 it
798 * means that the bound at the offset is the upper bound for a
799 * range not covered by any partition (other than a possible
800 * default partition). In hash partitioning, the same means no
801 * partition has been defined for the corresponding remainder
802 * value.
803 *
804 * In either case, the value is still part of the queried range of
805 * values, so mark to scan the default partition if one exists.
806 */
807 scan_default |= partition_bound_has_default(context->boundinfo);
808 continue;
809 }
810
811 result = bms_add_member(result, partindex);
812 }
}}}
* 参考リンク [#gcf14bec]
- [[PostgreSQL 12は ここがスゴイ! ~性能改善やpluggable storage engineなどの新機能を徹底解説~ (NTTデータ テクノロジーカンファレンス 2019講演資料)>https://www.slideshare.net/nttdata-tech/postgresql12-performance-improvement-pluggable-storage-engine-ntt-sawada]]- &size(11){&color(gray){on https://www.slideshare.net/nttdata-tech/postgresql12-performance-improvement-pluggable-storage-engine-ntt-sawada};};
- [[PostgreSQL: パーティションし過ぎには気をつけろ!>https://qiita.com/KazuyaTomita/items/c50d47111150e9b8d503]] - &size(11){&color(gray){on https://qiita.com/KazuyaTomita/items/c50d47111150e9b8d503};};
** PostgreSQLのパーティション改善に関するリンク [#o7fc0c3b]
- [[speeding up planning with partitions>https://www.postgresql.org/message-id/9d7c5112-cb99-6a47-d3be-cf1ee6862a1d@lab.ntt.co.jp]] - &size(11){&color(gray){on https://www.postgresql.org/};};
-- [[Build "other rels" of appendrel baserels in a separate step>https://github.com/postgres/postgres/commit/53bcf5e3dbeaed5baf9d09b124cf196d247c54ea]]- &size(11){&color(gray){on https://github.com/};};