osc shimane-2016-do-postgres-dream-of-graph-database
TRANSCRIPT
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
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
スロット定義
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
スロット定義
18
ロジカルデコーディング
PostgreSQL 9.4 から導入された基盤
WAL を任意形式にデコーディングする基盤デコーディング関数や、
デコードされた更新情報の反映は利用者が好きに開発できる。
PostgreSQL ロマン機能の一つですね。
19
Decoding Function
ざっくり言うと PostgreSQL の更新ログ (WAL) を
任意の形式の論理ログに変換する関数。
どういった形式に変換するのかは、Decoding Function 実装者まかせ。
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[])
論理レプリケーションスロットから変更内容を読み取る。
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 サーバが
よしなにやってくれる。
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
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!