osc shimane-2016-do-postgres-dream-of-graph-database

58
Do Postgres dream of Graph-Database? ぬこ@横浜 (@nuko_yokohama) OSC 島根 2016 バージョン

Upload: toshi-harada

Post on 15-Jan-2017

46 views

Category:

Technology


0 download

TRANSCRIPT

Do Postgres dream of Graph-Database?

ぬこ@横浜 (@nuko_yokohama)

OSC 島根 2016 バージョン

2

自己紹介「 PostgreSQL ラーメン」

でググってください。

3

日本 PostgreSQL ユーザ会の自称ゆる枠担当やってる

PostgreSQL おじさんです。

4

今日話すこと

PostgreSQL とグラフデータベースの話をしようじゃないか

5

DBMS の話とか地味ですまんな

でも、意外と面白いことができたりするんですよ。

6

Question.

PostgreSQL を使ったことある人

グラフデータベースを知ってる人

7

グラフデータベース?

8

グラフデータベース

データを「ノード」と「関連」で管理するの NoSQL の一種。ノード間を接続することでノード間の関連を表現する。

ノードのトラバースが容易に可能。

ぬこの主食の割合ご飯ラーメンうどんそばパン

2010年 2011年 2012年 2013年 2014年 2015年 2016年0

50

100

150

200

250

300

350

ぬこの年間ラー食回数

回数

まちがい せいかい

醤油 味噌 豚骨醤油 担々麺 塩 カレー その他0

200

400

600

800

1000

1200

地域 / 種別毎の分類

神奈川

東京

その他

種別

回数

ノード

関連

9

例えば Neo4j

ノード

関連

https://neo4j.com/Community Edition と Enterprise Edition があります。

例えば Neo4j

10

PostgreSQL からグラフデータを扱ってみよう

PostgreSQL の更新を Neo4j へ反映PostgreSQL から SQL で Neo4j を検索

11

PostgreSQL の更新を

Neo4j へ反映

12

Neo4j Server

Logideco4neo4j

PostgreSQL

Relational Model

Cypher Query

WAL(wal_level=logical) Logical

Replication Slot

Execute Cypher Query on REST

Convert Cypher Query

PostgreSQL WAL to Neo4j

Graph Model

olumn1 column2 column3

・・・ ・・・ ・・・

・・・・ ・・・・ ・・・

Get Replication Slot Data

Decoding FunctionConvert JSON

スロット定義

13

Neo4j Server

Logideco4neo4j

PostgreSQL

Relational Model

Cypher Query

WAL(wal_level=logical) Logical

Replication Slot

Execute Cypher Query on REST

Convert Cypher Query

PostgreSQL WAL to Neo4j

Graph Model

olumn1 column2 column3

・・・ ・・・ ・・・

・・・・ ・・・・ ・・・

Get Replication Slot Data

Decoding FunctionConvert JSON

スロット定義

14

Data Model

Neo4j にマッピングするためのPostgreSQL 上のデータモデル

非常にざっくり言うと、ノードテーブルと関連テーブルの

2 種のテーブルで構成する。

15

Data Modelノードテーブル

ノード ID(pk)ノードプロパティ ( 任意 )

関連テーブル関連 ID(pk)

起点ノードラベル(種類)起点ノード ID

関連タイプ (text)終点ノードラベル(種類)

終点ノード ID

ノードラベルをテーブル名に

マッピングするのが良さげ

今回は全ての関連タイプを

1 テーブルで管理

16

Neo4j Server

Logideco4neo4j

PostgreSQL

Relational Model

Cypher Query

WAL(wal_level=logical) Logical

Replication Slot

Execute Cypher Query on REST

Convert Cypher Query

PostgreSQL WAL to Neo4j

Graph Model

olumn1 column2 column3

・・・ ・・・ ・・・

・・・・ ・・・・ ・・・

Get Replication Slot Data

Decoding FunctionConvert JSON

スロット定義

17

ロジカルデコーディング

18

ロジカルデコーディング

PostgreSQL 9.4 から導入された基盤

WAL を任意形式にデコーディングする基盤デコーディング関数や、

デコードされた更新情報の反映は利用者が好きに開発できる。

PostgreSQL ロマン機能の一つですね。

19

Decoding Function

ざっくり言うと PostgreSQL の更新ログ (WAL) を

任意の形式の論理ログに変換する関数。

どういった形式に変換するのかは、Decoding Function 実装者まかせ。

20

Decoding Function

幸い PostgreSQL のソース内にはLogical Decoding の

サンプルが入っている。

contrib/test_decoding

21

Decoding Function

test_decoding を動作させてみる。デコードされた更新ログが、

特定の論理スロットに格納される。

SQL 関数を使って、このスロット内のデコードされた内容を読み取る。

22

Decoding Function例えば以下のような DML を発行するBEGIN;DROP TABLE IF EXISTS node;CREATE TABLE node(id int primary key, name text, age int, gender text, location text);COMMIT;

INSERT INTO node VALUES(1, 'Akagi',17,'Famale','Tokyo'),(2, 'Yamato',28,'Male','Tokyo'),(3, 'Musashi',27,'Male','Yokohama'),(4, 'Nagato',22,'Male','Kawasaki');

BEGIN;UPDATE node SET age = age + 1 WHERE id IN (1,2);DELETE FROM node WHERE id IN (3,4);SELECT * FROM node;COMMIT;

23

Decoding Function論理スロットから SQL 関数経由で更新ログを取得できる

logideco=# SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL); BEGIN 1959 COMMIT 1959 BEGIN 1960 table public.node: INSERT: id[integer]:1 name[text]:'Akagi' age[integer]:17 gender[text]:'Famale' location[text]:'Tokyo' table public.node: INSERT: id[integer]:2 name[text]:'Yamato' age[integer]:28 gender[text]:'Male' location[text]:'Tokyo' table public.node: INSERT: id[integer]:3 name[text]:'Musashi' age[integer]:27 gender[text]:'Male' location[text]:'Yokohama' table public.node: INSERT: id[integer]:4 name[text]:'Nagato' age[integer]:22 gender[text]:'Male' location[text]:'Kawasaki' COMMIT 1960 BEGIN 1961 table public.node: UPDATE: id[integer]:1 name[text]:'Akagi' age[integer]:18 gender[text]:'Famale' location[text]:'Tokyo' table public.node: UPDATE: id[integer]:2 name[text]:'Yamato' age[integer]:29 gender[text]:'Male' location[text]:'Tokyo' table public.node: DELETE: id[integer]:3 table public.node: DELETE: id[integer]:4 COMMIT 1961

test_decoding はDDL は未対応です。

24

Decoding Function

contrib/test_decoding は、テキスト形式で出力。

(見やすさ重視。サンプルだし)

でも、更新ログを他の DBMS に反映するには、不向きかも・・・?

25

Decoding Function

JSON のような汎用形式にしておけば、デコード結果を処理するのも楽になる。

↓表現能力は十分

可読性もある程度はある既存の JSON パーサが使える

既存のパーサが使えるのは仕様検討も実装も楽!

26

Decoding Function以下のような情報を含む

JSON データを生成すれば良さげ。コマンド タグ 更新対象の

スキーマと表挿入列と値 更新列と

更新後の値検索キー列とキー値

INSERT ○ ○ ○ - -

UPDATE ○ ○ - ○ ○

DELETE ○ ○ - - ○

トランザクションを意識するなら、BEGIN や COMMITも考慮する必要がある。

もう作っている人はいそうだが・・・

27

Decoding Function探したら、やっぱりありましたw

「 PostgreSQL の Logical Decoding 機能についての紹介」http://qiita.com/bwtakacy/items/d8461518a1770524e0d6

まとめを書いたのは@bwtakacy さん

pg_rman や pg_bulkload のメンテナやってる人です。

Deconding Function Plug-in wal2jsonhttps://github.com/eulerto/wal2json

名前のとおり WAL を JSON 形式にデコードしてくれる。せっかくなので、今回はこれを使ってみる。

28

Decoding Functionjson2wal の使用例

例えば以下のような DML を発行するneo4j=# BEGIN;BEGINneo4j=# INSERT INTO node_person VALUES ('2005','Shinano','Famale','Kawasaki','22');INSERT 0 1neo4j=# INSERT INTO rel_person_person VALUES ('10028','2005','love','1001');INSERT 0 1neo4j=# UPDATE node_person SET location = 'Yokohama' WHERE uid = '2005';UPDATE 1neo4j=# DELETE FROM rel_person_person WHERE rid = '10028';DELETE 1neo4j=# DELETE FROM node_person WHERE uid = '2005';DELETE 1neo4j=# COMMIT;COMMIT

29

Decoding Functionjson2wal の使用例

レプリケーションスロットから、取得したデコード結果トランザクション全体で 1 つの JSON 文書になっている

neo4j=# SELECT data FROM pg_logical_slot_get_changes('neo4j', NULL, NULL); {"xid":1976,"change":[ {"kind":"insert","schema":"public","table":"node_person","columnnames":["uid","name","gender","location","age"],"columntypes":["text","text","text","text","text"],"columnvalues":["2005","Shinano","Famale","Kawasaki","22"]} ,{"kind":"insert","schema":"public","table":"rel_person_person","columnnames":["rid","f_uid","type","t_uid"],"columntypes":["text","text","text","text"],"columnvalues":["10028","2005","love","1001"]} ,{"kind":"update","schema":"public","table":"node_person","columnnames":["uid","name","gender","location","age"],"columntypes":["text","text","text","text","text"],"columnvalues":["2005","Shinano","Famale","Yokohama","22"],"oldkeys":{"keynames":["uid"],"keytypes":["text"],"keyvalues":["2005"]}} ,{"kind":"delete","schema":"public","table":"rel_person_person","oldkeys":{"keynames":["rid"],"keytypes":["text"],"keyvalues":["10028"]}} ,{"kind":"delete","schema":"public","table":"node_person","oldkeys":{"keynames":["uid"],"keytypes":["text"],"keyvalues":["2005"]}} ]}

赤字が 1 つの DML によるログ情報

30

Neo4j Server

Logideco4neo4j

PostgreSQL

Relational Model

Cypher Query

WAL(wal_level=logical) Logical

Replication Slot

Execute Cypher Query on REST

Convert Cypher Query

PostgreSQL WAL to Neo4j

Graph Model

olumn1 column2 column3

・・・ ・・・ ・・・

・・・・ ・・・・ ・・・

Get Replication Slot Data

Decoding FunctionConvert JSON

スロット定義

31

Logideco4neo4j必要なパーツ

PostgreSQL クライアントライブラリJSON パーサ

http クライアントライブラリ

これが揃えば、どんな言語で組んでも OK今回は C 言語で組んでみたよ!

32

Logideco4neo4j

Logical Decoding 内容からNeo4j への反映に必要な処理

レプリケーションスロットへの検索検索結果から Cypher Query を生成

Cypher Query を REST API で Neo4j へこれを繰り返す

33

Logideco4neo4j

レプリケーションスロットからデコードされた WAL を

読み込む SQL 関数を発行する

http://www.postgresql.jp/document/9.5/html/functions-admin.html#functions-replication

関数名 説明

pg_create_logical_replication_slot(slot_name name, plugin name)

論理レプリケーションスロットを生成

pg_logical_slot_get_changes(slot_name name, upto_lsn pg_lsn, upto_nchanges int, VARIADIC options text[])

論理レプリケーションスロットから変更内容を読み取る。

34

Logideco4neo4j

デコード結果( JSON )を json-c 等のJSON パーサで解析して挿入・更新・削除用の

Neo4j Cypher クエリを生成する

35

Logideco4neo4jノード挿入の Cypher Query への変換例

INSERT INTO public.person (nid, name, gender, location, age) VALUES ('10001','ぬこ','Male','Yokohama','48')

{"kind":"insert","schema":"public","table":"person","columnnames":["nid","name","gender","location","age"],"columntypes":["text","text","text","text","text"],"columnvalues":["10001","ぬこ","Male","Yokohama","48"]}

CREATE (n:person { nid:"10001",name:"ぬこ",gender:"Male",location:"Yokohama",age:"48" })

{"query":"CREATE (n:person { nid:\"10001\",name:\"ぬこ\",gender:\"Male\",location:\"Yokohama\",age:\"48\" }) "}

SQL

論理ログ

Cypher

REST API

更新も削除も同様に REST API に変換

36

Logideco4neo4j関連挿入の Cypher Query への変換例

INSERT INTO relationship VALUES ('100001', 'employ', 'company', '1001', 'person', '10001');

{"kind":"insert","schema":"public","table":"relationship","columnnames":["rid","type","f_label","f_uid","t_label","t_uid"],"columntypes":["text","text","text","text","text","text"],"columnvalues":["100001","employ","company","1001","person","10001"]}

MATCH (f:company {nid:"1001"}),(t:person {nid:"10001"}) MERGE (f)-[e:employ {rid:"100001"}]->(t)

query={"query":"MATCH (f:company {nid:\"1001\"}),(t:person {nid:\"10001\"}) MERGE (f)-[e:employ {rid:\"100001\"}]->(t)"}

SQL

論理ログ

Cypher

REST API

更新も削除も同様に REST API に変換

37

Logideco4neo4j

Neo4j Cypher クエリを含むREST API パラメータを

libcurl などの HTTP ライブラリ経由で発行すると、後は Neo4j サーバが

よしなにやってくれる。

38

デモ

39

PostgreSQL の更新ログをNeo4j に適用できた。

やったね♪

40

PostgreSQL からSQL で

Neo4j を検索

41

ForeignData Wrapper

42

PostgreSQL の外部にあるデータに対して

標準的な SQL クエリでアクセスするための

PostgreSQL ロマン機能

43

Neo4j のデータをSQL で検索するための

Foreign Data Wrapper を開発しないといけない。

44

Neo4j Server

Logideco4neo4j

PostgreSQL

Cypher Query

WAL(wal_level=logical) Logical

Replication Slot

Execute Cypher Query on REST

Convert Cypher Query

olumn1 column2 column3

・・・ ・・・ ・・・

・・・・ ・・・・ ・・・

Get Replication Slot Data

Decoding FunctionConvert JSON

Neo4j Foriegn Data Wrapper

Defined Cypher Query

Execute Cypher Query on REST

Generate PostgreSQL Record

PostgreSQLRecord

JSONResult

Provide Cypher Result View

SQL

CypherQuery

スロット定義

45

というか、以前 Neo4j に

接続する FDW を作っていた

https://github.com/nuko-yokohama/neo4j_fdw

46

何故か PostgreSQL Wiki に載ってる・・・

メンテナンスをサボっているので、 PostgreSQL 9.5以降に対応してません・・・

47

neo4j_fdw外部データラッパを登録

外部サーバと外部テーブルを定義CREATE EXTENSION neo4j_fdw;

CREATE SERVER foo FOREIGN DATA WRAPPER neo4j_fdw OPTIONS (url 'http://localhost:7474/db/data/cypher');

CREATE FOREIGN TABLE persons ( uid text, name text, gender text, age text, company text ) SERVER foo OPTIONS (query '{"query":"MATCH (c:company)-[e:employ]->(p:person) RETURN p.nid, p.name, p.gender, p.age, c.name"}');

あとは persons テーブルに SELECT 文を発行すれば OK!

48

デモ

49

PostgreSQL からSQL を使って

Neo4j の検索ができた。

やったね♪

50

と、いうことで

51

ロジカルデコーディングとneo4j_fdwを組み合わせて

PostgreSQL 経由でグラフデータベースNeo4j を扱ってみた

52

で、何の役に立つのそれ?

面白ければ、いいのです。

53

このデモはともかく、ロジカルデコーディングやForeign Data Wrapper で

PostgreSQL と異種 DBMS 間連携が簡単にできるかも!?

54

おしまい

55

補足(というか蛇足)

56

FDW を良く知っている人

からのツッコミ

57

あれ? FDW にも更新 I/F あるじゃん

(PostgreSQL 9.3以降 )

58

汎用的に SQL DML からNeo4j の Cypher Query へ変換する仕組みを考えないと

いけないのが面倒…

面倒なので放置中…(だからロジデコ使った)