Upload
toshi-harada
View
347
Download
1
Embed Size (px)
Citation preview
中国 DB 勉強会(2015-01-31)
Lighting-Talk
インガオホー!jsonb + postgres_fdw
ぬこ@横浜 (@nuko_yokohama)
ドーモ、勉強会出席者の
ミナ=サンぬこ@横浜です某通信系暗黒ソフトウェアコーポで働いてる
おおむね善良で社会的に害はないサラリマン!
今日はマツエ殺伐都市へ
お呼び頂きアリガトネ!
最近、 NJSLYR を読み始めたニュービーです。
なのでこの LT は NJSLYR 的なコトダマ混入成分過多!そして、このスライドは頭のう指数が大幅低下!
コワイ!
本題
JSONB データ型とpostgres_fdw の組み合わせ!
ちょっとやめないか
JSONB データ型
・・・については今日の発表やPGCon.jp/JPUG 勉強会など
おおよそ過去の発表で既に拝聴
postgres_fdwhttp://www.postgresql.jp/document/9.3/html/postgres-fdw.html
主開発者の花田=サンとPostgreSQL コミュニティの
タツジンの見事なワザマエが光る一品。
実際スゴイ!
postgres_fdw外部の PostgreSQL サーバに格納されたデータを
自分の DB 内のテーブルであるかのようにアクセスするために使用する、外部データラッパ
類似モジュールとして dblink があるが、postgres_fdw のほうが、より自然なクエリが書ける。
参照だけでなく、更新 (insert/update/delete) も可。
PostgreSQL 9.3 からサポートされている。と、古事記 PostgreSQL 文書に書かれている。
例えばpostgres_fdw を使って
別データベース上の表との結合ヤッター!
しかし
通常の表で使える機能が外部表で全て使える
わけではない。備えよう。
その 1COPY
イヤーッ!
グワーッ!foo=# COPY test FROM '/tmp/json.txt';ERROR: cannot copy to foreign table "test"foo=#
外部テーブルに直接 COPY はできない。
表
リモートサーバ
外部表
ローカルサーバ
JSON データ COPY
COPY の問題は以下のジツを使って回避可能
1. リモートテーブルに直接 COPY2. INSERT 文を使用
3. file_fdw+ バルク INSERT(3 . の方法の詳細は次ページで説明)
備えよう。
file_fdw+ バルク INSERTこんなアトモスフィアなシェル作成!
#!/bin/shDBNAME=$1FOREIGN_TABLE_ARG=$2LOAD_FILE_NAME=$3LOAD_TABLE_NAME=$4
CREATE_SERVER_SQL="CREATE SERVER __cp_server FOREIGN DATA WRAPPER file_fdw"psql ${DBNAME} -c "${CREATE_SERVER_SQL}"
CREATE_FOREIGN_TABLE_SQL="CREATE FOREIGN TABLE __cp_table ( ${FOREIGN_TABLE_ARG} ) SERVER __cp_server OPTIONS (filename '${LOAD_FILE_NAME}')"psql ${DBNAME} -c "${CREATE_FOREIGN_TABLE_SQL}"
INSERT_SQL="INSERT INTO ${LOAD_TABLE_NAME} (SELECT data FROM __cp_table )"psql ${DBNAME} -c "${INSERT_SQL}"
DROP_FOREIGN_TABLE_SQL="DROP FOREIGN TABLE __cp_table"psql ${DBNAME} -c "${DROP_FOREIGN_TABLE_SQL}"
DROP_SERVER_SQL="DROP SERVER __cp_server"psql ${DBNAME} -c "${DROP_SERVER_SQL}"
contrib/file_fdw の外部サーバ / 外部表を定義外部表を SELECT で全件検索した結果を
INSERT 文に流し込む。最後に外部表と外部サーバを爆発四散!
file_fdw+ バルク INSERT前ページのシェルを実行!
$ ./fdw_cp foo "data jsonb" "/tmp/json.txt" "test"CREATE SERVERCREATE FOREIGN TABLEINSERT 0 100000DROP FOREIGN TABLEDROP SERVER$
ゴウランガ!COPY で使うファイルをそのまま使って
外部表へロード可能!(COPY より遅いのは仕方がない。いいね?)
このシェルは postgres_fdw 専用というわけでなく挿入操作が可能な FDWなら使用重点!
その 2TRUNCATEイヤーッ!
グワーッ!外部テーブルに対する
TRUNCATEはどの FDW でもできないっぽい。
ざんねんながらリモートの実表に TRUNCATE 必須!
その 3外部テーブル内の
JSONB に対する検索
これは説明重点。
例えば、外部テーブルのソースとなるサーバに以下のような
JSONB カラムを持つtest テーブルがあるとする。
確かにあるのだ!
bar=# \d test Table "public.test" Column | Type | Modifiers --------+-------+----------- data | jsonb | Indexes: "test_id_idx" btree ((data ->> 'id'::text)) "test_idx" gin (data)
ローカルな問い合わせならbtree 式インデックスも
gin インデックスも使える。bar=# EXPLAIN SELECT * FROM test WHERE data->>'id' = '10000'; QUERY PLAN ------------------------------------------------------------------------- Index Scan using test_id_idx on test (cost=0.42..8.44 rows=1 width=70) Index Cond: ((data ->> 'id'::text) = '10000'::text)(2 rows)
bar=# EXPLAIN SELECT * FROM test WHERE data @> '{"id":10000}'; QUERY PLAN -------------------------------------------------------------------------- Bitmap Heap Scan on test (cost=28.77..336.47 rows=100 width=70) Recheck Cond: (data @> '{"id": 10000}'::jsonb) -> Bitmap Index Scan on test_idx (cost=0.00..28.75 rows=100 width=0) Index Cond: (data @> '{"id": 10000}'::jsonb)(4 rows)
あからさまにインデックス検索なのだ!
しかし外部テーブル経由だと
@> 演算子 { キー : 値 } の場合WHERE 句を pushdown して、
リモート側でも GIN インデックスを使用foo=# EXPLAIN ANALYZE SELECT * FROM test WHERE data @> '{"id":10000}'; QUERY PLAN -------------------------------------------------------------- Foreign Scan on test (cost=100.00..128.29 rows=1 width=32) (actual time=0.579..0.579 rows=1 loops=1) Planning time: 0.052 ms Execution time: 0.867 ms(3 rows)
Time: 1.204 ms
外部テーブルから返却された時点で1件になっていることに注目重点な!
実際速い!
->>' キー ' 演算子 値 の場合WHERE 句は pushdown されず、
リモート側でフルスキャン全件取得!アイエエエ!フルスキャン!
フルスキャンナンデ!foo=# EXPLAIN ANALYZE SELECT * FROM test WHERE data->>'id' = '10000'; QUERY PLAN ---------------------------------------------------------------------- Foreign Scan on test (cost=100.00..159.93 rows=7 width=32) (actual time=31.028..273.480 rows=1 loops=1) Filter: ((data ->> 'id'::text) = '10000'::text) Rows Removed by Filter: 99999 Planning time: 0.046 ms Execution time: 273.581 ms(5 rows)
そして外部テーブルからは全件 (10万件 ) 返却されている!あからさまにフルスキャンなのだ!ヤンナルネ・・・
実際遅い!
postgres_fdw 外部テーブルに渡すWHERE 句の書き方によって
Pushdwon されたりされなかったりする。
おかしいと思いませんか?あなた
古事記 PostgreSQL 文書にはこう書かれている
F.31.4. リモート問合せの最適化
外部サーバからのデータ転送量を削減するため、postgres_fdw はリモート問合せを最適化しようと試みます。 これは問い合わせのWHERE 句をリモートサーバに送出する事、およびクエリで必要とされていないカラムを取得しない事により行われます。 問い合わせの誤作動のリスクを下げるため、ビルトインのデータ型、演算子、関数だけを用いたものでない限り、リモートサーバにWHERE 句は送出されません。また、WHERE 句で使われる演算子と関数は IMMUTABLEでなければなりません。
カラム ->>' キー名 ' への比較演算だとダメっぽい?無視できぬ記述だ!
要するに、特定の条件式がPushdown されない問題は
「これはバグではない。いいね?」「アッハイ」
JSON 型 /XML 型等と postgres_fdw は相性は良くないかも・・・
ということでpostgres_fdw は便利ですが
使用時には注意重点な。という話でした。オタッシャデー!