postgres_fdwで異バージョン接続/外部表のカスケード
PostgreSQL 9.6/9.5のpostgres_fdwの差異を調べていて、ふと気になったことを調べてみた。
異バージョン間でFDW
PostgreSQL 9.6/9.5のpostgres_fdwの差異を調べていて、ふと気になったのだけど、異なるバージョン間をpostgres_fdwで接続ってできたっけ。
ということでやってみた。
ローカルサーバが9.6、リモートサーバが9.5の場合。
bench=# EXPLAIN ANALYZE VERBOSE SELECT AVG(a.data1), AVG(a.data2) FROM table_a a JOIN table_b b ON (a.id = b.id) WHERE a.data1 = 10000 ; QUERY PLAN ----------------------------------------------------------------------------------------------------------------------------------------------------- Aggregate (cost=342.59..342.60 rows=1 width=64) (actual time=2.176..2.176 rows=1 loops=1) Output: avg(a.data1), avg(a.data2) -> Foreign Scan (cost=100.00..341.78 rows=161 width=8) (actual time=2.151..2.153 rows=11 loops=1) Output: a.data1, a.data2 Relations: (public.table_a a) INNER JOIN (public.table_b b) Remote SQL: SELECT r1.data1, r1.data2 FROM (public.table_a r1 INNER JOIN public.table_b r2 ON (((r1.id = r2.id)) AND ((r1.data1 = 10000)))) Planning time: 1.714 ms Execution time: 11.359 ms (8 rows)
おお、フツーに結合SQLを9.5のリモートサーバに投げているな。
ローカルサーバが9.5、リモートサーバが9.6
bench=# EXPLAIN ANALYZE VERBOSE SELECT AVG(a.data1), AVG(a.data2) FROM table_a a JOIN table_b b ON (a.id = b.id) WHERE a.data1 = 10000 ; QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------------ Aggregate (cost=349.93..349.94 rows=1 width=8) (actual time=1152.246..1152.246 rows=1 loops=1) Output: avg(a.data1), avg(a.data2) -> Hash Join (cost=238.80..349.12 rows=161 width=8) (actual time=48.058..1152.208 rows=10 loops=1) Output: a.data1, a.data2 Hash Cond: (b.id = a.id) -> Foreign Scan on public.table_b b (cost=100.00..197.75 rows=2925 width=4) (actual time=0.349..1065.369 rows=1000000 loops=1) Output: b.id, b.data1, b.data2 Remote SQL: SELECT id FROM public.table_b -> Hash (cost=138.66..138.66 rows=11 width=12) (actual time=0.284..0.284 rows=10 loops=1) Output: a.data1, a.data2, a.id Buckets: 1024 Batches: 1 Memory Usage: 9kB -> Foreign Scan on public.table_a a (cost=100.00..138.66 rows=11 width=12) (actual time=0.274..0.277 rows=10 loops=1) Output: a.data1, a.data2, a.id Remote SQL: SELECT id, data1, data2 FROM public.table_a WHERE ((data1 = 10000)) Planning time: 0.128 ms Execution time: 1152.649 ms (16 rows)
ローカル側は9.5なので、当然ながらJoin-pushdownの実行計画が存在しない。
なので、リモートの9.6に対して、結合のないクエリを複数回発行して、その結果をローカル側で結合する。
まあ、当たり前の結果ではあるけど、一応試してみたかったので。
postgres_fdw のカスケード
ていうか、postgres_fdwのソースって実表だけなんだっけ?と疑問に思ったので、ソースとしてビュー、マテリアライズド・ビュー、そして外部表を試してみることにした。
構成はこんな感じ。
(´-`).oO (IMPORT FOREIGN SCHEMAがサポートされて、こういう検証が本当に楽になったよ・・・)
さて、この構成でサーバ 96_l の外部テーブルに対してクエリを投げてみる。
詳細は省略するけど、table_a, table_b, table_a_v, table_a_v, table_c, table_d 全て外部テーブル経由で検索はできた。
もちろん、更新不可能なビューや、マテリアライズド・ビューをソースとする外部表を更新しようとするとエラーになる。
bench=# UPDATE table_a_v SET data1 = 50000; ERROR: cannot update view "table_a_v" DETAIL: Views containing TABLESAMPLE are not automatically updatable. HINT: To enable updating the view, provide an INSTEAD OF UPDATE trigger or an unconditional ON UPDATE DO INSTEAD rule. CONTEXT: Remote SQL command: UPDATE public.table_a_v SET data1 = 50000 STATEMENT: UPDATE table_a_v SET data1 = 50000;
bench=# UPDATE table_a_mv SET data1 = 50000; ERROR: cannot change materialized view "table_a_mv" CONTEXT: Remote SQL command: UPDATE public.table_a_mv SET data1 = 50000 STATEMENT: UPDATE table_a_mv SET data1 = 50000;
で、せっかく9.6を使っているので、Join pushdownが効くのかどうか試してみた。
table_a, table_bを結合したクエリは当然結合クエリが 96_r にpushdownされてます。
bench=# EXPLAIN ANALYZE VERBOSE SELECT AVG(a.data1), AVG(a.data2) FROM table_a_v a JOIN table_b b ON (a.id = b.id) WHERE a.data1 = 1000 ; QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------------------------ Aggregate (cost=342.59..342.60 rows=1 width=64) (actual time=6.023..6.023 rows=1 loops=1) Output: avg(a.data1), avg(a.data2) -> Foreign Scan (cost=100.00..341.78 rows=161 width=8) (actual time=6.016..6.016 rows=0 loops=1) Output: a.data1, a.data2 Relations: (public.table_a_v a) INNER JOIN (public.table_b b) Remote SQL: SELECT r1.data1, r1.data2 FROM (public.table_a_v r1 INNER JOIN public.table_b r2 ON (((r1.id = r2.id)) AND ((r1.data1 = 1000)))) Planning time: 0.248 ms Execution time: 7.353 ms (8 rows)
では、96_rr → 96_r → 96_l のように外部表をカスケードした場合はどうなるのか。
お、96_rr サーバまで結合クエリがきちんとpushdownされてますね。
bench=# EXPLAIN ANALYZE VERBOSE SELECT AVG(c.data1), AVG(c.data2) FROM table_c c JOIN table_d d ON (c.id = c.id) WHERE c.data1 = 1000; QUERY PLAN ---------------------------------------------------------------------------------------------------------------------------------------------------- Aggregate (cost=294.93..294.94 rows=1 width=64) (actual time=1.105..1.105 rows=1 loops=1) Output: avg(c.data1), avg(c.data2) -> Foreign Scan (cost=100.00..277.86 rows=3413 width=8) (actual time=1.101..1.101 rows=0 loops=1) Output: c.data1, c.data2 Relations: (public.table_c c) INNER JOIN (public.table_d d) Remote SQL: SELECT r1.data1, r1.data2 FROM (public.table_c r1 INNER JOIN public.table_d r2 ON (((r1.id = r1.id)) AND ((r1.data1 = 1000)))) Planning time: 0.140 ms Execution time: 1.787 ms (8 rows)
96_r, 96_rr サーバのデータベースに contrib/auto_explain を入れて、実際にどんなSQLが発行されてみるか確認してみる。
96_r サーバで実行されたSQL
LOG: duration: 0.392 ms plan: Query Text: DECLARE c1 CURSOR FOR SELECT r1.data1, r1.data2 FROM (public.table_c r1 INNER JOIN public.table_d r2 ON (((r1.id = r1.id)) AND ((r1.data1 = 1000)))) Foreign Scan (cost=100.00..277.86 rows=3413 width=8) Relations: (public.table_c r1) INNER JOIN (public.table_d r2)
96_rr サーバで実行されたSQL
LOG: duration: 0.018 ms plan: Query Text: DECLARE c1 CURSOR FOR SELECT r1.data1, r1.data2 FROM (public.table_c r1 INNER JOIN public.table_d r2 ON (((r1.id = r1.id)) AND ((r1.data1 = 1000)))) Nested Loop (cost=0.29..2553.33 rows=100000 width=8) -> Index Scan using c_data1 on table_c r1 (cost=0.29..12.33 rows=1 width=8) Index Cond: (data1 = 1000) Filter: (id = id) -> Seq Scan on table_d r2 (cost=0.00..1541.00 rows=100000 width=0)
うん、きちんと結合条件がpushdownされてますね。
知ってる人には当たり前かもしれないけど、自分で確認して納得できたので良かった。