Download - Do postgres-dream-of-graph-database
Do Postgres dream of Graph-Database?
ぬこ@横浜 (@nuko_yokohama)
2
自己紹介「 PostgreSQL ラーメン」
でググってください。
3
日本 PostgreSQL ユーザ会のゆる枠担当 ( 自称 ) やってるPostgreSQL おじさんです。
4
今日話すこと
5
今日はこの本に書いてあるような話はしません。
この本
6
PostgreSQL からグラフデータを扱ってみよう
7
グラフデータベース?
8
グラフデータベース
データを「ノード」と「関連」で管理するの NoSQL の一種。
ノード間を関連で接続することでノード間の関係性を表現する。
ノードのトラバースが容易に可能。
9
例えば Neo4j
ノード
関連
https://neo4j.com/Community Edition と Enterprise Edition があります。
例えば Neo4j
10
PostgreSQL の更新を
Neo4j へ反映
11
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
スロット定義
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
Data Model
Neo4j にマッピングするためのPostgreSQL 上のデータモデル
非常にざっくり言うと、ノードテーブルと関連テーブルの
2 種のテーブルで構成する。
14
Data Modelノードテーブル
ノード ID(pk)ノードプロパティ ( 任意 )
関連テーブル関連 ID(pk)
起点ノードラベル(種類)起点ノード ID
関連タイプ (text)終点ノードラベル(種類)
終点ノード ID
ノード種類をテーブル名に
マッピングするのが良さげ
今回は全ての関連タイプを
1 テーブルで管理
15
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
スロット定義
16
ロジカルデコーディング
17
ロジカルデコーディングPostgreSQL 9.4 から導入された基盤
WAL を任意形式にデコーディングする基盤デコーディング方法や、
デコードされた更新情報の反映は利用者が好きに開発できる。
夢が広がりんぐ機能
18
Decoding Function
ざっくり言うと PostgreSQL の更新ログ (WAL) を
任意の形式の論理ログに変換する関数。
どういった形式に変換するのかは、Decoding Function 実装者まかせ。
19
Decoding Function
幸い PostgreSQL のソース内にはLogical Decoding の
サンプルが入っている。
contrib/test_decoding
20
Decoding Function
実際に動作させてみる。デコードされた更新ログが、
pg_logical_replication_slot に格納されている。
SQL 関数を使って、このスロット内のデコードされた内容を読み取る。
21
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;
22
Decoding Function以下のような更新ログが出力される
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 は未対応です。
23
Decoding Function
contrib/test_decoding は、見やすさを重視した形式で出力。
(サンプルだし)
でも、更新ログを他の DBMS に反映するには、不向きかも・・・?
24
Decoding Function
JSON のような汎用形式にしておけば、デコード結果を処理するのも楽になる。
↓表現能力は十分
可読性もある程度はある既存の JSON パーサが使える
既存のパーサが使えるのは仕様検討も実装も楽!
25
Decoding Function以下のような情報を含む
JSON データを生成すれば良さげ。コマンド タグ 更新対象の
スキーマと表挿入列と値 更新列と
更新後の値検索キー列とキー値
INSERT ○ ○ ○ - -
UPDATE ○ ○ - ○ ○
DELETE ○ ○ - - ○
トランザクションを意識するなら、BEGIN や COMMIT も考慮する必要がある。
もう作っている人はいそうだが・・・
26
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 形式にデコードしてくれる。せっかくなので、今回はこれを使ってみる。
27
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
28
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 によるログ情報
29
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
スロット定義
30
Logideco4neo4j必要なパーツ
PostgreSQL クライアントライブラリJSON パーサ
http クライアントライブラリ
これが揃えば、どんな言語で組んでも OK今回は C 言語で組んでみたよ!
31
Logideco4neo4j
Logical Decoding 内容からNeo4j への反映に必要な処理
レプリケーションスロットへの検索検索結果から Cypher Query を生成
Cypher Query を REST API で Neo4j へこれを繰り返す
32
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[])
論理レプリケーションスロットから変更内容を読み取る。
33
Logideco4neo4j
デコード結果( JSON )を json-c 等のJSON パーサで解析して挿入・更新・削除用の
Neo4j Cypher クエリを生成する
34
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 に変換
35
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 に変換
36
Logideco4neo4j
Neo4j Cypher クエリを含むREST API パラメータを
libcurl などの HTTP ライブラリ経由で発行すると、後は Neo4j サーバが
よしなにやってくれる。
37
デモ
38
PostgreSQL の更新ログをNeo4j に適用できた。
やったね♪
39
PostgreSQL からSQL で
Neo4j を検索
40
ForeignData Wrapper
41
PostgreSQL の外部にあるデータに対して
標準的な SQL クエリでアクセスするための夢が広がりんぐ基盤
42
Neo4j のデータをSQL で検索するための
Foreign Data Wrapper を開発する。
43
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
スロット定義
44
というか、以前 Neo4j に
接続する FDW を作っていた
https://github.com/nuko-yokohama/neo4j_fdw
45
何故か PostgreSQL Wiki に載ってる・・・
メンテナンスをサボっているので、 PostgreSQL 9.5 以降に対応してません・・・
46
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!
47
デモ
48
PostgreSQL からSQL を使って
Neo4j の検索ができた。
やったね♪
49
と、いうことで
50
ロジカルデコーディングとneo4j_fdw を組み合わせて
PostgreSQL からグラフデータベースNeo4j を扱えた!
それに意味があるのかどうかは
わりとどうでもいい
51
おしまい
52
補足(というか蛇足)
53
FDW を良く知っている人
からのツッコミ
54
あれ? FDW にも更新 I/F あるじゃん
(PostgreSQL 9.3 以降 )
55
汎用的に SQL DML からNeo4j の Cypher Query へ変換する仕組みを考えないと
いけないのが面倒…
面倒なので放置中…(だからロジデコ使った)