myna jpug study 20160220-postgresql-json-datatype

Post on 06-Jan-2017

1.515 Views

Category:

Technology

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

MyNA JPUG 合同勉強会(2016-02-20)

PostgreSQL でJSON 型+ α の話

ぬこ@横浜 (@nuko_yokohama)

2

自己紹介「 PostgreSQL ラーメン」

でググってください。

3

自己紹介 ( こんなの作ってた )拡張名 説明ksj 漢数字で演算可能な誰得なデータ型ntext 日本語正規化比較や、 typo を含む類似検索を行う

誰得なデータ型と textsearch 用パッチneo4j_fdw グラフデータベース neo4j への誰得なコネクタhb_worker PostgreSQL 上で数当てゲームする誰得な

Back ground workerpg_sulog スーパーユーザの操作のみをロギングしたり、

ブロックしたりする、誰得な HOOK 拡張

PostgreSQL を無駄に使うのが趣味 (?) です。一部は https://github.com/nuko-yokohama に登録してあります。

4

アジェンダPostgreSQL と非構造化データ

JSON と JSONBPostgreSQL 9.5 での改良

XML と hstoreJSON/JSONB の話がメインです

5

PostgreSQL と非構造化データ

6

構造化の観点から見た分類

あり

なし

完全

不完全

構造化データ構造化定義

半構造化データ

非構造化データ

構造が定義されている

構造の定義が不完全

構造が定義されている

構造の定義がない

7

構造化の観点から見た分類種別 内容 例構造化データ 何らかの定義(スキーマ定義等)に

より、構造を事前に決定しておくデータ。

CREATE TABLE 文で定義されたテーブル

半構造化データ 事前の定義はあるが、定義内の一部の構造が存在しなくても良いデータ。

XML スキーマで定義された XML 文書

非構造化データ 事前の構造定義を持たないデータ。 任意の JSON 文書

通常、 RDBMS では上記の種別のデータのうち、構造化データを扱う。

8

PostgreSQL と非構造データ型

PostgreSQL で通常扱うのは、「構造化データ」

PostgreSQL ではデータ型として「半構造化データ」「非構造化データ」(以降、両方とも「非構造化データ」と記述)を扱うしくみも用意されている。

9

PostgreSQL と非構造データ型

PostgreSQL で使用可能な、非構造化データ型は以下の 4 種類

型名 対応バージョン 位置づけ 備考XML 8.2 ~ 本体機能 libxml2 依存hstore 8.3 ~ contribJSON 9.2 ~ 本体機能JSONB 9.4 ~ 本体機能

まずこっちの話からします

10

JSON と JSONB

11

JSON とは

JavaScript Object NotationXML よりも軽量

構造化されたデータを文字列で表現可能

12

PostgreSQL におけるJSON への取り組み

9.2 から順調に進化。 9.5 でも…?

●JSON 型の導入。●2 つの JSON 型構築関数。●JSON 内の値を使った条件検索はできなかった。

●JSON 型関数・演算子の大幅な強化。

●JSON データ型へのパスによるアクセス。

●JSON 内の値を使った条件検索が可能に。

●JSONB 型の導入。●検索の高速化●独自演算子の追加●GIN インデックス対応

9.29.3

9.4

13

JSON 型でできること

JSON 文字列のパースキーによる値の取り出しパスによる値の取り出し

PostgreSQL 配列や行との変換etc ・・・

14

JSON 文字列のパース例正しい JSON 文字列

誤った JSON 文字列

※パースは JSON/JSONB 型に変換されるときに行われる。

jsonb=# SELECT '{"key1":"value1", "key2":[100, 20, 5]}'::json; json ---------------------------------------- {"key1":"value1", "key2":[100, 20, 5]}(1 row)

jsonb=# SELECT '{"key1":"value1", "key2":[100, 20, ]}'::json;ERROR: invalid input syntax for type json at character 8DETAIL: Expected JSON value, but found "]".CONTEXT: JSON data, line 1: {"key1":"value1", "key2":[100, 20, ]...STATEMENT: SELECT '{"key1":"value1", "key2":[100, 20, ]}'::json;ERROR: invalid input syntax for type jsonLINE 1: SELECT '{"key1":"value1", "key2":[100, 20, ]}'::json;

カンマの後に値がない…

15

キーによる値の取り出しキーから JSON オブジェクトを取得

キーから文字列を取得

jsonb=# SELECT data->'Email' as email , data->'Full Name' as fullname FROM test ORDER BY id LIMIT 3; email | fullname ----------------------+------------------- "Laverna@junius.io" | "Carolyne Kohler" (null) | "Paul Weber DVM" "Elbert@norma.co.uk" | "Florence Murphy"(3 rows)

jsonb=# SELECT data->>'Email' as email , data->>'Full Name' as fullname FROM test ORDER BY id LIMIT 3; email | fullname --------------------+----------------- Laverna@junius.io | Carolyne Kohler (null) | Paul Weber DVM Elbert@norma.co.uk | Florence Murphy(3 rows)

16

jsonb=# SELECT '{"person":{"name":{"first":"Tom","last":"Lane"},"age":59}}'::json #>> '{person, name, first}'; ?column? ---------- Tom(1 row)

パスによる値の取り出しperson, name, first のパスで取得

person

name

first last

age

Tom Lane

59

(root)

17

jsonb=# SELECT '{"Name":{"First":"Oleg","Last":"Bartunov"},"Country":"Russia","Contributes":["hstore","JSON","JSONB","GIN"]}'::json #>> '{Contributes,2}'; ?column? ---------- JSONB(1 row)

パスによる値の取り出しContributes の 2番目 (0相対 ) を取得

Contributes

name

first last

Oleg

hstore

(root)

JSON JSONB GIN

[0] [1] [2] [3]

Bartunov

18

式インデックスとの組合せ式インデックスの設定jsonb=# SELECT data FROM json_t LIMIT 1; { "Email": "Laverna@junius.io", "Created At": "1987-08-21T18:42:02.269Z", "Country": "Paraguay", "Id": 0, "Full Name": "Carolyne Kohler"}(1 row)

jsonb=# CREATE INDEX json_id_idx ON json_t USING btree ((data->>'Id'));CREATE INDEXjsonb=# \d json_t Unlogged table "public.json_t" Column | Type | Modifiers --------+---------+----------------------------------------------------- id | integer | not null default nextval('json_t_id_seq'::regclass) data | json | Indexes: "json_id_idx" btree ((data ->> 'Id'::text)) PostgreSQL は

「式」の結果に対してインデックスが設定できます!

19

式インデックスとの組合せ式インデックスを利用した検索jsonb=# EXPLAIN ANALYZE SELECT data FROM json_t WHERE data->>'Id' = '1000'; Bitmap Heap Scan on json_t (cost=4.68..130.11 rows=50 width=32) (actual time=0.078..0.078 rows=1 loops=1) Recheck Cond: ((data ->> 'Id'::text) = '1000'::text) Heap Blocks: exact=1 -> Bitmap Index Scan on json_id_idx (cost=0.00..4.66 rows=50 width=0) (actual time=0.072..0.072 rows=1 loops=1) Index Cond: ((data ->> 'Id'::text) = '1000'::text) Planning time: 0.248 ms Execution time: 0.106 ms(7 rows)

20

その他の JSON 関数

PostgreSQL 配列と JSON配列との変換行から JSON への変換

JSON から PostgreSQL 行への展開キー集合の取得

キーと値を与えて JSON を構築他にもいろいろ

あるよ!

21

PostgreSQL 配列と JSON 配列との変換

jsonb=# SELECT array_to_json(ARRAY[1, 3.14, -1.2e3]); array_to_json ---------------- [1,3.14,-1200](1 row)

jsonb=# TABLE bar; id | n_datas | t_datas ----+------------------+------------------------ 1 | {1.32,9.76,5.55} | {bdho,fjoal} 2 | {6.43,0.48} | {vbwdahoi,3dsai,cfjia} 3 | {} | {}(3 rows)

jsonb=# SELECT array_to_json(bar.t_datas) FROM bar; array_to_json ------------------------------ ["bdho","fjoal"] ["vbwdahoi","3dsai","cfjia"] [](3 rows)

build_object_json()などと組み合わせて使うこともできる

22

行から JSON への変換

jsonb=# TABLE foo; id | n_data | t_data ----+--------+---------- 1 | 8.93 | 366fd4cf 2 | 5.23 | 3f0a243b 3 | (null) | (null)(3 rows)

jsonb=# SELECT row_to_json(foo.*) FROM foo; row_to_json -------------------------------------------- {"id":1,"n_data":8.93,"t_data":"366fd4cf"} {"id":2,"n_data":5.23,"t_data":"3f0a243b"} {"id":3,"n_data":null,"t_data":null}(3 rows)

列名をキー名に、列値を値に変換する

列値が null の場合は、JSON の null に変換

23

JSON から PostgreSQL 行への展開

jsonb=# select * from json_each_text('{"id": 2, "age": 59, "name": {"last": "Lane", "first": "Tom"}}'); key | value ------+---------------------------------- id | 2 age | 59 name | {"last": "Lane", "first": "Tom"}(3 rows)

※内側のキー ("first", "last") と値は展開しない。 ※ key, value という固定の列名として返却される。 ※ key も value も TEXT 型として返却される。

24

キー集合の取得

jsonb=# TABLE test; {"id": 1, "name": {"first": "Oleg"}, "distribute": ["GIN", "hstore", "json", "jsonb"]} {"id": 2, "age": 59, "name": {"last": "Lane", "first": "Tom"}} {"id": 3, "name": {"nickname": "nuko"}, "distribute": ["ksj", "neo4jfdw"]}

jsonb=# SELECT DISTINCT jsonb_object_keys(data) FROM test; distribute age id name

以下のような JSONB を含む全レコードからキーを取得する。

jsonb_object_keys関数の結果

※内側のキー ("first", "last") は取得できない点に注意!※自分で Outer キーを指定して取得した JSONB に対して  jsonb_object_keys を発行することは可能。

25

キーと値を与えて JSON を構築

jsonb=# SELECT json_build_object('name',' ぬこ ','age', 17, 'hobby', ' ラーメン ');

{"name" : "ぬこ ", "age" : 17, "hobby" : "ラーメン "}

奇数番目の引数にキー名を、偶数番目の引数に値を与える。

jsonb=# SELECT jsonb_build_object( 'name',jsonb_build_object('first',' ぬこ ','last','横浜 '),

'age', 17, 'hobby', ' ラーメン ');

{"age": 17, "name": {"last": "横浜 ", "first": " ぬこ "}, "hobby": "ラーメン "}

jsonb_build_object()関数のネストも可能

データベース内の他の値からJSON/JSONB 型を構築する場合に

重宝する関数!

26

JSON と JSONB

27

JSON 型格納形式は文字列

JSONB 型格納形式は規定のバイナリ形式

データ型の導入専用演算子の追加

28

JSON と JSONB の格納状態例

00001fb0 95 7b 22 61 61 61 22 20 3a 20 22 41 41 41 41 22 |.{"aaa" : "AAAA"|00001fc0 2c 20 22 62 62 62 31 22 20 3a 20 7b 22 63 63 63 |, "bbb1" : {"ccc|00001fd0 63 22 3a 20 22 43 43 43 43 31 22 7d 2c 20 22 62 |c": "CCCC1"}, "b|00001fe0 62 62 32 22 20 3a 7b 22 63 63 63 63 22 20 3a 20 |bb2" :{"cccc" : |00001ff0 22 43 43 43 43 32 22 7d 20 7d 00 00 00 00 00 00 |"CCCC2"} }......|

00001fa0 b5 03 00 00 20 03 00 00 80 04 00 00 00 04 00 00 |.... ...........|00001fb0 00 04 00 00 00 16 00 00 50 18 00 00 50 61 61 61 |........P...Paaa|00001fc0 62 62 62 31 62 62 62 32 41 41 41 41 00 01 00 00 |bbb1bbb2AAAA....|00001fd0 20 04 00 00 80 05 00 00 00 63 63 63 63 43 43 43 | ........ccccCCC|00001fe0 43 31 00 00 00 01 00 00 20 04 00 00 80 05 00 00 |C1...... .......|00001ff0 00 63 63 63 63 43 43 43 43 32 00 00 00 00 00 00 |.ccccCCCC2......|

{"aaa" : "AAAA", "bbb1" : {"cccc": "CCCC1"}, "bbb2" :{"cccc" : "CCCC2"} }

入力 JSON 文字列

JSON 型の格納状態

JSONB 型の格納状態

PostgreSQL 9.5.1で確認

入力文字そのまま

バイナリ化されたデータが格納される

29

バイナリ形式で格納すると

何が嬉しいのか?

30

JSONB は格納効率を優先はしていない格納サイズに関しては

むしろ JSON より大きくなるケースもある。

JSONB の格納性能もJSON と比較すると遅い。

31

JSONB は検索効率を優先

JSON 関数を使ってキーによる検索を行ったときに

JSON と比較して非常に高速(性能検証結果は後述)

32

JSONB データ型JSON 自体にはデータ型の概念がある。

PostgreSQL の JSONB 型もデータ型に一部対応している。

JSON 型 対応する PostgreSQL 型 備考

文字型 text エンコーディングの問題は注意。なお、キーは文字型扱い。

数値型 numeric 浮動小数点型の NaN や Infinity の表記は許容されない。

時刻型 (未対応) PostgreSQL では文字列扱い。

真偽値 boolean 小文字の true/false のみ許容。(on/off とかもダメ )

null - 小文字 null のみ許容。SQL NULL とは別概念。

33

JSONB は入力したJSON 文字列は保持されない。

JSON は入力文字列をそのまま保持する。

34

JSONB 入力結果が保持されない例(数値型)

jsonb=# INSERT INTO jsonb_t VALUES (1, '{"key":1, "value":100}'),(2, '{"key":2, "value":100.0}'),(3, '{"key":3, "value":1.00e2}'),(4, '{"key":4, "value":1.000e2}');INSERT 0 4jsonb=# SELECT * FROM jsonb_t ; id | data ----+---------------------------- 1 | {"key": 1, "value": 100} 2 | {"key": 2, "value": 100.0} 3 | {"key": 3, "value": 100} 4 | {"key": 4, "value": 100.0}(4 rows)

小数なしのnumeric として同値

小数点ありのnumeric として同値

格納後に取り出すと指数表記でなくなる

35

JSON だと単に文字列として格納されているだけなので、入力形式そのままで出力される

jsonb=# INSERT INTO json_t VALUES jsonb-# (1, '{"key":1, "value":100}'),jsonb-# (2, '{"key":2, "value":100.0}'),jsonb-# (3, '{"key":3, "value":1.00e2}'),jsonb-# (4, '{"key":4, "value":1.000e2}')jsonb-# ;INSERT 0 4jsonb=# SELECT * FROM json_t; id | data ----+---------------------------- 1 | {"key":1, "value":100} 2 | {"key":2, "value":100.0} 3 | {"key":3, "value":1.00e2} 4 | {"key":4, "value":1.000e2}(4 rows)

36

JSONB 入力結果が保持されない例(重複キー、キー名の順序)

jsonb=# SELECT '{"key_z":99, "key_q": 20, "key_q" :25, "key_a" : 3}'::json, '{"key_z":99, "key_q": 20, "key_q" :25, "key_a" : 3}'::jsonb;-[ RECORD 1 ]---------------------------------------------- json | {"key_z":99, "key_q": 20, "key_q" :25, "key_a" : 3} jsonb | {"key_a": 3, "key_q": 25, "key_z": 99}

jsonb=# SELECT '{"key_x":[ 1, 400, 8888, 2, 99, 25, 3]}'::json, '{"key_x":[ 1, 400, 8888, 2, 99, 25, 3]}'::jsonb ;-[ RECORD 1 ]---------------------------------- json | {"key_x":[ 1, 400, 8888, 2, 99, 25, 3]} jsonb | {"key_x": [1, 400, 8888, 2, 99, 25, 3]}

同一階層で重複したキーがある場合、後に記述したもののみ有効となる。また、キー名でソートされている。

配列の場合、順序は保証される。

最初の” key_q” は保持されない!

37

JSONB 入力結果が保持されない例(空白の扱い)

jsonb=# SELECT '{"key_z" : 99, "key q": 20, "key_a" : "aa aa"}'::json, '{"key_z" : 99, "key q": 20, "key_a" : "aa aa"}'::jsonb; -[ RECORD 1 ]-------------------------------------------------- json | {"key_z" : 99, "key q": 20, "key_a" : "aa aa"} jsonb | {"key q": 20, "key_a": "aa aa", "key_z": 99}

キーまたは値でない JSON 文字列中の空白は、JSONB では無視される。また、 JSONB の検索結果として出力される JSON 文字列ではキーと値のセパレータである“ :” の後に 1つ空白文字を入れるという規則で文字列を生成している。

38

JSONB 専用演算子 (9.4)演算子 意味

<@ 左辺の JSON が右辺のキー&値の組を含むかを評価する。

@> 右辺の JSON が左辺のキー&値の組を含むかを評価する。

? 左辺の JSON が右辺の(一つの)キーまたは要素を含むかどうかを評価する。

|? 左辺の JSON が右辺のキーまたは要素を 1つでも含むかどうかを評価する。( ANY 評価)

&? 左辺の JSON が右辺のキーまたは要素を全て含むかどうかを評価する。( ALL 評価)

39

JSONB 専用演算子 (9.5追加 )演算子 意味

|| 指定したキーが存在しなければ、キー&値の組み合わせを追加し、存在すれば指定したキー&値の組み合わせを置換する。

- 指定したキーと合致するキー&値の組み合わせを削除する。。

- 指定した番号の配列要素を削除する。

#- 指定したパスに合致するキー&値の組み合わせ、または配列要素を削除する。

詳細は後で!

40

@> 演算子と GIN

GIN インデックスとの組み合わせで、お手軽な

インデックスの作成が可能

41

GIN インデックス

汎用転置インデックス配列型での利用

全文検索等でも利用※ これも JSONB と同じ開発チームで開発している。

http://www.sai.msu.su/~megera/wiki/Gin

42

@>と GIN インデックス

jsonb=# SELECT data FROM jsonb_t LIMIT 1; {"Id": 0, "Email": "Laverna@junius.io", "Country": "Paraguay", "Full Name": "Carolyne Kohler", "Created At": "1987-08-21T18:42:02.269Z"}(1 row)

jsonb=# CREATE INDEX jsonb_idx ON jsonb_t USING gin (data jsonb_ops);CREATE INDEXjsonb=# \d jsonb_t Unlogged table "public.jsonb_t" Column | Type | Modifiers --------+---------+------------------------------------------------------ id | integer | not null default nextval('jsonb_t_id_seq'::regclass) data | jsonb | Indexes: "jsonb_idx" gin (data)

JSONB カラム自体に GIN インデックスを設定

JSONB 列自体にGIN インデックスを

設定する。

43

この状態で @> 演算子(右辺にはキーと値の組み合わせ)を使うと GIN インデックスによる効率的な検索が可能になる

jsonb=# EXPLAIN SELECT data->'Full Name' as fullname, data->'Country' as country FROM jsonb_t WHERE data @> '{"Country" : "Monaco"}'; QUERY PLAN -------------------------------------------------------------------------- Bitmap Heap Scan on jsonb_t (cost=20.08..54.18 rows=10 width=160) Recheck Cond: (data @> '{"Country": "Monaco"}'::jsonb) -> Bitmap Index Scan on jsonb_idx (cost=0.00..20.07 rows=10 width=0) Index Cond: (data @> '{"Country": "Monaco"}'::jsonb) Planning time: 0.074 ms(5 rows)

@>と GIN インデックス

JSONB カラムに @> 演算子で条件を記述

@> + GIN の組み合わせはキーが不定でも有効!

44

JSONB 型の実装

45

JSON 型と JSONB 型の処理

JSON 型と JSONB 型のデータ格納データ取得

JSON関数処理の処理イメージを次ページ以降に示す。

46

JSON 型の処理イメージJSON 文字列

JSONツリーJSON パーサ

JSON 型(文字列)

JSON 文字列

関数の結果

(そのまま出力)

格納時

取り出し時

JSON関数

JSON パーサ関数処理

JSONツリー

PostgreSQLストレージ

パーサではチェックのみを行う

処理時にもパースが必要

47

JSONB 型の処理イメージJSON 文字列

JSONツリーJSON パーサ

JSONB 型(バイナリ)

JSON 文字列

関数の結果

文字列化

格納時

取り出し時

JSONB関数

関数処理

PostgreSQLストレージシリアライズ

処理する時にはバイナリを直接処理

パースとシリアライズを行う

48

JSON ツリーparseState=0

pstate

res

res

type=jbvObject val.object.nPairs=3

keytype=jbvString

pair[0]

valtype=jbvString

len=3 val

AAA

len=2 val

aa

keytype=jbvString

valtype=jbvString

pair[1]

keytype=jbvString

pair[2]

len=2 val

bb

・・・

上記の JSON文字列をパースすると以下のようなツリー構造が生成される

※ PostgreSQL 9.4-beta2の場合

{"aa":"AAA","bb":"BBB","cc":"CCC"}

49

格納サイズ

さっき説明した JSON と JSONB の処理内容から格納サイズを予想すると

以下のようになる。

格納サイズは JSONB よりもJSON のほうが小さい?

( JSONB はツリー構造情報も保持)

50

以下のような JSON データ{ "Email": "Laverna@junius.io", "Created At": "1987-08-21T18:42:02.269Z", "Country": "Paraguay","Id":"0", "Full Name": "Carolyne Kohler"}

測定内容TEXT/JSON/JSONB カラムのみをもつテーブルを作成

上記 1万件を 10回 COPY した後のテーブルサイズを pg_relation_size() で測定

51

格納サイズPostgreSQL 9.5.1で確認

text

json

jsonb

0 2 4 6 8 10 12 14 16 18 20

19.0

19.0

18.7

10万件データロード後のテーブルサイズ (MB)

9.4 では jsonb が少し大きかったが、

9.5 で少し改善された?

52

性能比較

さっき説明した JSON と JSONB の処理内容から処理性能を予想すると

以下のようになる。

格納処理は JSON のほうが高速単なる SELECT は JSON のほうが高速?JSON 関数を使った場合は JSONB が高速

53

測定環境

PC:Let's note SX4(SSD)OS:CentOS 7.0( メモリ 4GB)

PostgreSQL パラメータはデフォルト

54

以下のような JSON データ{ "Email": "Laverna@junius.io", "Created At": "1987-08-21T18:42:02.269Z", "Country": "Paraguay", "Id": 0, "Full Name": "Carolyne Kohler"}

測定内容JSON/JSONB カラムをもつテーブルを UNLOGGED で作成

上記 1万件を 10回 COPYしたときの平均性能SELECT(10 万件 ) の平均性能

SELECT(10 万件 ) + JSON 演算子を使ったときの平均性能btree vs GIN インデックス検索性能

55

TEXT

JSON

JSONB

0 5 10 15 20 25 30 35 40 45 50

14.65

17.49

43.71

1 万件ロード平均時間 (ms)

COPY 処理時間

TEXT と JSON の差分が 10000件分の JSON パース時間?JSON と JSONB の差分が 10000件文のシリアライズ時間?

PostgreSQL 9.5.1で測定

JSONB の挿入は結構遅い・・・

56

JSON

JSONB

0 2 4 6 8 10 12 14 16 18 20

14.12

14.08

10 万件 COUNT(*)平均時間 (ms)

COUNT(*) 処理時間PostgreSQL 9.5.1で確認

10万件格納されたテーブルに対して、SELECT COUNT(*) FROM テーブル名 を実行

ほぼ同じですねー

57

JSON

JSONB

0 20 40 60 80 100 120 140 160

136.55

25.42

10 万件 data->>'Country' 平均時間 (ms)

SELECT 処理時間

10万件格納されたテーブルに対して、SELECT data->'Country' FROM テーブル名 を実行

PostgreSQL 9.5.1で確認

JSON関数使用時はJSONB が圧倒的に早い!

58

btree vs GINjsonb=# EXPLAIN ANALYZE SELECT * FROM test_jsonb WHERE data->>'Country' = 'Monaco'; Bitmap Heap Scan on test_jsonb (cost=12.29..1240.26 rows=500 width=161) (actual time=0.088..0.364 rows=310 loops=1) Recheck Cond: ((data ->> 'Country'::text) = 'Monaco'::text) Heap Blocks: exact=280 -> Bitmap Index Scan on jb_bi (cost=0.00..12.17 rows=500 width=0) (actual time=0.055..0.055 rows=310 loops=1) Index Cond: ((data ->> 'Country'::text) = 'Monaco'::text) Planning time: 0.052 ms Execution time: 0.402 ms

jsonb=# EXPLAIN ANALYZE SELECT * FROM test_jsonb WHERE data @> '{"Country":"Monaco"}'; Bitmap Heap Scan on test_jsonb (cost=28.77..362.50 rows=100 width=161) (actual time=0.650..1.004 rows=310 loops=1) Recheck Cond: (data @> '{"Country": "Monaco"}'::jsonb) Heap Blocks: exact=280 -> Bitmap Index Scan on jb_gi (cost=0.00..28.75 rows=100 width=0) (actual time=0.613..0.613 rows=310 loops=1) Index Cond: (data @> '{"Country": "Monaco"}'::jsonb) Planning time: 0.052 ms Execution time: 1.043 ms(7 rows)

GIN インデックス使用時

btree 式インデックス使用時

特定のキーを検索条件に使うと分かっている場合は

btree 式インデックスが有利!

59

測定結果まとめ

格納: JSON が高速SELECT :ほぼ同じ?

JSON関数: JSONB が高速GIN より btree 式インデックスが高速

60

格納効率優先なら JSON検索効率優先なら JSONB

入力形式を保持するなら JSON

⇒だいたいの場合、 JSONB で OK?

結局、 JSON と JSONB はどう使い分けるのか?

61

PostgreSQL 9.5 での改良

62

JSONB 専用演算子 (9.5追加 )演算子 意味

|| 指定したキーが存在しなければ、キー&値の組み合わせを追加し、存在すれば指定したキー&値の組み合わせを置換する。

- 指定したキーと合致するキー&値の組み合わせを削除する。。

- 指定した番号の配列要素を削除する。

#- 指定したパスに合致するキー&値の組み合わせ、または配列要素を削除する。

文書の部分更新が可能に!

63

JSONB 型の改善9.4 まで(部分更新が出来ないので、更新結果の再構築 + 型変換が必要)

SELECT data FROM test WHERE (data->>'id')::int = 222; data ---------------------------------------------------------------- {"id": 222, "age": 18, "name": "nuko", "location": "Yokohama"}(1 row)

UPDATE test SET data = json_build_object( 'id', data->>'id', 'name', data->>'name', 'location', data->>'location', 'age', 20)::jsonb WHERE (data->>'id')::int = 222;UPDATE 1SELECT data FROM test WHERE (data->>'id')::int = 222; data ------------------------------------------------------------------ {"id": "222", "age": 20, "name": "nuko", "location": "Yokohama"}(1 row)

書くのも面倒。性能もよろしくない。

キーが不定の場合対応できない?

64

JSONB 型の改善9.5 から(部分更新演算子でシンプルに記述可能)

SELECT data FROM test WHERE (data->>'id')::int = 222; data ---------------------------------------------------------------- {"id": 222, "age": 18, "name": "nuko", "location": "Yokohama"}(1 row)

UPDATE test SET data = data || '{"age":20}' WHERE (data->>'id')::int = 222;UPDATE 1SELECT data FROM test WHERE (data->>'id')::int = 222; data ---------------------------------------------------------------- {"id": 222, "age": 20, "name": "nuko", "location": "Yokohama"}(1 row)

部分更新演算子でスッキリ!性能上も有利!

65

JSON 型の改善部分更新演算子による性能改善効果

比較用 UPDATE jsonb_build_object 9.5 演算子0

1000

2000

3000

4000

5000

6000

7000

8000

9000

10000

3743

8574

5362

JSONB 100 万件更新時間

更新時間

(ms)

こうかはばつぐんだ!

66

JSON 型の想定用途

67

多様なログの収集(スキーマが規定できないケース)

JSON化

ログ

ログ

ログ

JSONBカラムに格納GIN/btreeインデックス設定

ログ検索アプリケーション

rsyslog等ログ

既存の表JSONB表

68

微妙なテーブル設計の救済?CREATE TABLE foo { id integer primary key, name text,・・・ memo1 text, memo2 text, memo3 text,・・・ memo100 text}

CREATE TABLE foo { id integer primary key, name text,・・・ memo jsonb}

※きちんと設計をしていれば不要だろうけど・・・

とりあえず予備カラム群

作っちゃう奴~

JSON 型にまとめてしまおう

69

MongoDB 等のドキュメント指向 DB の代替?

or

本当にいいのかな・・

70

当たり前の話ですが、JSON/JSONB を

過剰に使うのも考えもの。きちんと DB を設計した上で

うまくハマるところで使うべき。  ,j;;;;;j,. --- 一、 `  ― --‐ 、 _ l;;;;;; { ;;;;;; ゝ T 辷 i フ i     f' 辷 j ァ  !i;;;;; なんでも JSON に入れればいい・・・  ヾ ;;; ハ    ノ        .::!l リ ;;r ゙   `Z;i    〈 .,_..,.       ノ ;;;;;;;;>   そんなふうに考えていた時期が    ,; ぇハ、 、 _,. ー - 、 _',.     , f゙ : Y;;f.    俺にもありました   ~'' 戈ヽ   `二 ´    r'´:::.   `!

71

PostgreSQL の他の非構造データ型

せっかくなのでXML 型と hstore 型についても、

ちょっと話します

72

XML 型

XML を格納するデータ型本体機能 (configure で指定 )格納時に XML パースを行なう型自体に比較演算機能はないxpath によるアクセスが可能SQL 関数による XML 型構築libxml2 ライブラリに依存

「 XML滅べ」って言ってもなかなか滅びない・・・

73

XML 型の処理イメージXML 文字列

XML パーサ

XML 型(文字列)

XML 文字列

関数の結果

(そのまま出力)

格納時

取り出し時

xpath関数

XML パーサ関数処理

xpath処理

PostgreSQLストレージ

パーサではチェックのみを行う

74

XML 型でできること

XML 文字列のパースと格納xpath による結果の取り出し

SQL 関数による XML 型の構築

75

XML 型の使用例XML 型カラムを持つテーブルother=# \d xml_t Table "public.xml_t" Column | Type | Modifiers --------+---------+---------------------------------------------------- id | integer | not null default nextval('xml_t_id_seq'::regclass) data | xml |

other=# SELECT data FROM xml_t; <rdb_t><email>Laverna@junius.io</email><created_at>1987-08-21T18:42:02.269Z</created_at><country>Paraguay</country><id>0</id><full_name>Carolyne Kohler</full_name></rdb_t> <rdb_t><email>Nakia_Rolfson@cecelia.ca</email><created_at>1989-03-16T14:37:36.825Z</created_at><country>France</country><id>1</id><full_name>Paul Weber DVM</full_name></rdb_t> <rdb_t><email>Elbert@norma.co.uk</email><created_at>1980-02-19T04:16:52.113Z</created_at><country>Uzbekistan</country><id>2</id><full_name>Florence Murphy</full_name></rdb_t>(3 rows)

76

XML 型の使用例xpath 関数による XML 文書からの抽出

(例) xpath関数は XML配列を返却するので、テキストを取り出す場合に、 TEXT

配列にキャストして最初の要素を取り出すなどの操作が必要。(例)名前空間 url と prefix の組を配列化して xpath関数に渡す必要がある。

other=# SELECT (xpath('/rdb_t/email/text()', data)::text[])[1] as email,(xpath('/rdb_t/full_name/text()', data)::text[])[1] as fullnameFROM xml_t; email | fullname --------------------------+----------------- Laverna@junius.io | Carolyne Kohler Nakia_Rolfson@cecelia.ca | Paul Weber DVM Elbert@norma.co.uk | Florence Murphy(3 rows)

XML 型を使うのはちょい面倒・・・

77

hstore 型

Key-Value store データ型contrib モジュールネストはできない。

部分更新インタフェースあり※作者は JSONB と同じ Oleg 氏

シンプルなKey-Value 型

78

hstore 型の処理イメージhstore 文字列

hstore パーサ

hstore 型(バイナリ)

hstore 文字列

関数の結果

文字列化

格納時

取り出し時

hstore関数

関数処理

PostgreSQLストレージシリアライズ

パースとバイナリ化を行う

処理する時にはバイナリを直接処理

パースとバイナリ化を行う

79

hstore 型でできること

hstore 文字列のパースと格納キーによる値の取り出し

キーと値のセットで追加 / 更新指定キーのキー&値を削除レコードからの生成

JSON への変換GIN インデックス対応

80

hstore 型の使用例hstore 型カラムを持つテーブルother=# \dx List of installed extensions Name | Version | Schema | Description ---------+---------+------------+-------------------------------------------------- hstore | 1.3 | public | data type for storing sets of (key, value) pairs plpgsql | 1.0 | pg_catalog | PL/pgSQL procedural language(2 rows)

other=# \d hstore_t Table "public.hstore_t" Column | Type | Modifiers --------+---------+------------------------------------------------------- id | integer | not null default nextval('hstore_t_id_seq'::regclass) data | hstore |

CREATE EXTENSION で登録が必要です。

81

hstore 型の使用例hstore 型カラムの検索other=# SELECT data FROM hstore_t; "id"=>"0", "email"=>"Laverna@junius.io", "country"=>"Paraguay", "full_name"=>"Carolyne Kohler", "created_at"=>"1987-08-21T18:42:02.269Z" "id"=>"1", "email"=>"Nakia_Rolfson@cecelia.ca", "country"=>"France", "full_name"=>"Paul Weber DVM", "created_at"=>"1989-03-16T14:37:36.825Z" "id"=>"2", "email"=>"Elbert@norma.co.uk", "country"=>"Uzbekistan", "full_name"=>"Florence Murphy", "created_at"=>"1980-02-19T04:16:52.113Z"(3 rows)

“キー名” =>” 値”の形式で出力される。

82

hstore 型の使用例hstore 型カラムから特定のキーのみ抽出

条件式を使う例

other=# SELECT data->'email' as email, data->'full_name' as full_name FROM hstore_t; email | full_name --------------------------+----------------- Laverna@junius.io | Carolyne Kohler Nakia_Rolfson@cecelia.ca | Paul Weber DVM Elbert@norma.co.uk | Florence Murphy(3 rows)

other=# SELECT data->'id' as id, data->'email' as email FROM hstore_t WHERE data->'id' = '1'; id | email ----+-------------------------- 1 | Nakia_Rolfson@cecelia.ca(1 row)

XML 型 /xpath 関数を使うよりシンプル!

“->” という演算子にキー名を与えて取り出す

83

XML, hstore, JSON/JSONB 比較データ型 表現能力 格納サイズ 参照性能 更新性能XML ◎ △ △ △

hstore △ ○ ◎ ◎

JSON ○ ○ ○ ◎

JSONB ○ ○ ◎ ○

XML は表現能力は高いが扱いにくいhstore はシンプルだが速いJSONB は良いとこ取り?⇒ 用途で使い分け可能

84

まとめ

85

PostgreSQL は非構造化データを扱う手段がいくつかある

XML/hstore/JSON/JSONB のどれを使うかは要件次第

非構造化データ型を本当に使うべきかどうかもきちんと検討しよう

86

ご清聴ありがとうございました

87

Question?

88

参考情報●本家文書:“ PostgreSQL 9.5 Documentation”

http://www.postgresql.org/docs/9.5/static/index.html●本家Wiki :“ What's new in PostgreSQL 9.5”

https://wiki.postgresql.org/wiki/What%27s_new_in_PostgreSQL_9.5●日本ヒューレット・パッカード株式会社 篠田さん●「篠田の虎の巻」“ PostgreSQL 9.5 新機能検証結果”

http://community.hpe.com/t5/日本のお客様向け-エンタープライズ-トピックス/篠田の虎の巻その4を公開-大幅に機能強化されたPostgreSQL-9-5の世界へ飛び込もう/ba-p/6797055?profile.language=ja#.VchDOE0w-Sw

●SRA OSS, Inc. 高塚さん~徹底検証報告~ 次期メジャーバージョン PostgreSQL 9.5 の実力 http://www.sraoss.co.jp/event_seminar/2015/PostgreSQL9.5-report.pdf

●NTTDATA 澤田さん PostgreSQL 9.5 新機能紹介http://www.slideshare.net/hadoopxnttdata/postgresql-95-new-features-nttdata

●Michael Paquier さんの blog● http://michael.otacoo.com/●”7 つのデータベース 7 つの世界”ISBN 978-274-06907-6

●・・・ And Many Postgres Users

むしろ、これらが必見ですよ!

top related