TL;DR
- 前回までの学びを活用して,RDBMSのデータベースを,neo4jに取り込む手順を紹介します
- MariaDBに格納したRDBMSベンチマークTPC-Hのテーブルを,neo4j用に変換して取り込みます
- TPC-Hに限らず,考え方は他のRDBMSのデータベース構造にも応用できます
neo4jコンテナの起動
- この記事の手順は,メモリをたくさん消費したり,ulimit制限に引っかかったりします
- そのため,以下のようにneo4jのコンテナにいくつかオプションを付与して,問題を回避します
docker run -d \
--ulimit nofile=64000 \
-p 7474:7474 -p 7687:7687 \
-v ${PWD}/neo4j/data:/var/lib/neo4j/data \
-v ${PWD}/neo4j/import:/var/lib/neo4j/import \
-e NEO4J_dbms_memory_pagecache_size=6G \
-e NEO4J_dbms_memory_heap_max__size=6G \
neo4j
オプション | 説明 |
---|---|
--ulimit nofile=64000 | ulimitサイズを64000にする |
-v ${PWD}/neo4j/data:/var/lib/neo4j/data | neo4jのデータベースの不揮発ボリューム |
-v ${PWD}/neo4j/import:/var/lib/neo4j/import | インポートするcsvを置くディレクトリ |
-e NEO4J_dbms_memory_pagecache_size=6G | ページキャッシュサイズを6GBにする |
-e NEO4J_dbms_memory_heap_max__size=6G | ヒープメモリサイズを6GBにする |
事前準備
-
データベースベンチマークTPC-HをMySQLで実行する(TPC-H v2.18.0)を参考に, sf=0.01 のサイズでデータベースを作成し,mariadbに登録しておきます
- sf=1でやったらデータの登録が終わらなくなったので,この記事では小さい(sf=0.01)データを扱います
テーブル間関係の確認
- TPC-Hのテーブル間の関連を確認します
- 矢印がhas-manyの関連です
- テーブル間の文字列は,関係を表す名前(自由に決められるもの)です
グラフノードの登録
- 以下の手順でNeo4jにグラフを作ります
- MariaDBからテーブルのcsvファイルを作成する
- Neo4jに,テーブル=ラベル,行=ノードとして,外部キー以外のエントリを書き込む
- Neo4jに外部キーの接続関係を登録する
1. MariaDBからテーブルの抜き出し
- TPC-Hは8つのテーブルから成るので,それぞれをcsvファイルで取り出します
- tpch-mysqlのtblファイルをそのまま使っても良いですが,コマンドが長くなるので,一度csvファイルに書き出すことにしました
for i in region nation customer supplier partsupp part lineitem orders
do
mysql -h 127.0.0.1 \
-u <MySQLユーザ> -p<MySQLユーザのパスワード> \
-e "select * from sf001.${i} into outfile '/tmp/${i}.csv' fields terminated by ',' optionally enclosed by '\"';";
done
-
/tmp/
に各テーブルがcsvとして書き出されます- データベース名が
sf001
でない場合は,適切に修正してください
- データベース名が
2. Neo4jにcsvファイルをインポートする
RDBMSのテーブル=GraphDBのラベル,エントリ=ノードとして,csvファイルをNeo4jに書き込みます.
-
neo4j-client
コマンドを使用してLOAD CSV
句を使用します- このとき,主キーは登録しますが,外部キーは登録しません
- 外部キーは次節の「関係」で表現します
-
USING PERIODIC COMMIT
句を追加して,コミットを頻度を落とし,メモリ節約をしています
neo4j>
USING PERIODIC COMMIT 500
LOAD CSV FROM 'file:///sf001/region.csv' as line
CREATE(:region{R_REGIONKEY:toInteger(line[0]),
R_NAME: line[1],
R_COMMENT: line[2]});
USING PERIODIC COMMIT 500
LOAD CSV FROM 'file:///sf001/nation.csv' as line
CREATE(:nation{N_NATIONKEY:toInteger(line[0]),
N_NAME: line[1],
N_COMMENT: line[3]});
USING PERIODIC COMMIT 500
LOAD CSV FROM 'file:///sf001/customer.csv' as line
CREATE(:customer{C_CUSTKEY:toInt(line[0]),
C_NAME:line[1],
C_ADDRESS:line[2],
C_PHONE:line[4],
C_ACCTBAL:toFloat(line[5]),
C_MKTSEGMENT:line[6],
C_COMMENT:line[7]});
USING PERIODIC COMMIT 500
LOAD CSV FROM 'file:///sf001/supplier.csv' as line
CREATE(:supplier{S_SUPPKEY:toInteger(line[0]),
S_NAME:line[1],
S_ADDRESS:line[2],
S_PHONE:line[4],
S_ACCTBAL:toFloat(line[5]),
S_COMMENT:line[6]});
USING PERIODIC COMMIT 500
LOAD CSV FROM 'file:///sf001/part.csv' as line
CREATE(:part{P_PARTKEY:toInteger(line[0]),
P_NAME:line[1],
P_MFGR:line[2],
P_BRAND:line[3],
P_TYPE:line[4],
P_SIZE:toInteger(line[5]),
P_CONTAINER:line[6],
P_RETAILPRICE:toFloat(line[7]),
P_COMMENT:line[8]});
USING PERIODIC COMMIT 500
LOAD CSV FROM 'file:///sf001/partsupp.csv' as line
CREATE(:partsupp{PS_PARTKEY:toInteger(line[0]),
PS_SUPPKEY:toInteger(line[1]),
PS_AVAILQTY:toInteger(line[2]),
PS_SUPPLYCOST:toFloat(line[3]),
PS_COMMENT:line[4]});
USING PERIODIC COMMIT 500
LOAD CSV FROM 'file:///sf001/orders.csv' as line
CREATE(:orders{O_ORDERKEY:toInteger(line[0]),
O_ORDERSTATUS:line[2],
O_TOTALPRICE:toFloat(line[3]),
O_ORDERDATE:date(line[4]),
O_ORDERPRIORITY:line[5],
O_CLERK:line[6],
O_SHIPPRIORITY:toInteger(line[7]),
O_COMMENT:line[8]});
USING PERIODIC COMMIT 500
LOAD CSV FROM 'file:///sf001/lineitem.csv' as line
CREATE(:lineitem{L_ORDERKEY:toInteger(line[0]),
L_LINENUMBER:toInteger(line[3]),
L_QUANTITY:toFloat(line[4]),
L_EXTENDEDPRICE:toFloat(line[5]),
L_DISCOUNT:toFloat(line[6]),
L_TAX:toFloat(line[7]),
L_RETURNFLAG:line[8],
L_LINESTATUS:line[9],
L_SHIPDATE:date(line[10]),
L_COMMITDATE:date(line[11]),
L_RECEIPTDATE:date(line[12]),
L_SHIPINSTRUCT:line[13],
L_SHIPMODE:line[14],
L_COMMENT:line[15]});
-
lineitem.csvを読むクエリの実行に時間がかかります
- awsのt2.xlargeでは1.5時間くらいかかりました
-
heapメモリが足りない場合には,以下のエラーメッセージが出力されます
<interactive>:0:0: error: There is not enough memory to perform the current task. Please try increasing 'dbms.memory.heap.max_size'
3. Neo4jに関係を登録する
- TPC-Hの「主キーと外部キーの関係」を,neo4jに登録します.
- 以下の手順で行います
- 外部キーを持つテーブルのcsvファイルを読み込む
- 1行ずつ,主キーを指定してneo4j上のノードを特定する
- 当該ノードが持つべき関係を,外部キーを主キーとして持つ別のノードに関連付ける
- 複雑に見えますが,下記のコマンドを見ると,なんとなく掴めます
neo4j>
USING PERIODIC COMMIT 500
LOAD CSV FROM 'file:///sf001/nation.csv' AS line
MATCH (n:nation{N_NATIONKEY: toInteger(line[0])})
MATCH (r:region{R_REGIONKEY: toInteger(line[2])})
CREATE (n)-[:AT]->(r);
USING PERIODIC COMMIT 500
LOAD CSV FROM 'file:///sf001/customer.csv' AS line
MATCH (c:customer{C_CUSTKEY: toInteger(line[0])})
MATCH (n:nation{N_NATIONKEY: toInteger(line[3])})
CREATE (c)-[:LOCATES]->(n);
USING PERIODIC COMMIT 500
LOAD CSV FROM 'file:///sf001/orders.csv' AS line
MATCH (o:orders{O_ORDERKEY: toInteger(line[0])})
MATCH (c:customer{C_CUSTKEY: toInteger(line[1])})
CREATE (o)-[:FROM]->(c);
USING PERIODIC COMMIT 500
LOAD CSV FROM 'file:///sf001/lineitem.csv' AS line
MATCH (l:lineitem{L_ORDERKEY:toInteger(line[0]), L_LINENUMBER:toInteger(line[3])})
MATCH (o:orders{O_ORDERKEY: toInteger(line[0])})
CREATE (l)-[:IN]->(o);
USING PERIODIC COMMIT 500
LOAD CSV FROM 'file:///sf001/supplier.csv' AS line
MATCH (s:supplier{S_SUPPKEY:toInteger(line[0])})
MATCH (n:nation{N_NATIONKEY:toInteger(line[3])})
CREATE (s)-[:LOCATES]->(n);
USING PERIODIC COMMIT 500
LOAD CSV FROM 'file:///sf001/partsupp.csv' AS line
MATCH(ps:partsupp{PS_PARTKEY:toInteger(line[0]), PS_SUPPKEY:toInteger(line[1])})
MATCH(s:supplier{S_SUPPKEY:toInteger(line[1])})
CREATE (ps)-[:FROM]->(s);
USING PERIODIC COMMIT 500
LOAD CSV FROM 'file:///sf001/partsupp.csv' AS line
MATCH(ps:partsupp{PS_PARTKEY:toInteger(line[0]), PS_SUPPKEY:toInteger(line[1])})
MATCH(p:part{P_PARTKEY:toInteger(line[0])})
CREATE (ps)-[:INCLUDES]->(p);
USING PERIODIC COMMIT 500
LOAD CSV FROM 'file:///sf001/lineitem.csv' AS line
MATCH (l:lineitem{L_ORDERKEY:toInteger(line[0]), L_LINENUMBER:toInteger(line[3])})
MATCH (ps:partsupp{PS_PARTKEY:toInteger(line[1]), PS_SUPPKEY:toInteger(line[2])})
CREATE (l)-[:IN]->(ps);
- 以上の手順で,neo4jにサイズsf=0.01のTPC-Hデータベースを登録することができました
neo4jのデータベースバックアップ
- データの登録に時間が掛かるので,作ったデータベースのバックアップを取ります
コンテナでneo4jを実行している場合
- バックアップ前にneo4jサーバを止めないといけないのですが,
neo4j start
で起動していない場合(コンテナ実行の場合)は,一度コンテナを削除する必要があります - コンテナを立て直す,neo4jの起動スクリプトの代わりに,
neo4j-admin
コマンドでダンプを作成します - 作成されるバックアップデータは,不揮発ボリュームに格納して,ホストからアクセスできるようにします
docker stop <neo4jコンテナ名>
docker rm -f <neo4jコンテナ名>
docker run --rm --ulimit nofile=64000 \
-p 7474:7474 \
-p 7687:7687 \
-v ${PWD}/neo4j/data:/var/lib/neo4j/data \
-v ${PWD}/neo4j/import:/var/lib/neo4j/import \
-e NEO4J_dbms_memory_pagecache_size=6G \
-e NEO4J_dbms_memory_heap_max__size=6G \
neo4j bin/neo4j-admin dump --database=graph.db --to=./data/tpch-sf001.dump
- ホストから見たときの
${PWD}/neo4j/data
にtpch-sf001.dump
が生成されています
ホストでneo4jを実行している場合
コンテナでない場合は,neo4j stop
コマンドでneo4jサーバを停止させます
-
neo4j-admin dump
コマンドでダンプを作成します
neo4j stop
neo4j-admin dump --database=graph.db --to=./data/tpch-sf001.dump
neo4jデータのリストア
- ダンプデータのリストアも,
neo4j-admin load
コマンドで,同様の方法で可能です.
コンテナでneo4jを実行する場合
- 一度コンテナを作ってから,ダンプファイルを不揮発ボリュームにコピーし,ダンプデータをロードします.その後,あらためてコンテナを立ち上げます
# 各種ディレクトリを作るために,一度コンテナを作る
docker run --ulimit nofile=64000 \
-p 7474:7474 \
-p 7687:7687 \
-v ${PWD}/neo4j/data:/var/lib/neo4j/data \
-v ${PWD}/neo4j/import:/var/lib/neo4j/import \
-e NEO4J_dbms_memory_pagecache_size=6G \
-e NEO4J_dbms_memory_heap_max__size=6G \
neo4j
# 不揮発ボリュームにダンプデータをコピー
cp tpch-sf001.dump ${PWD}/neo4j/data/
# 作ったコンテナを消す
docker stop <neo4jコンテナ名>
docker rm -f <neo4jコンテナ名>
# neo4j-admin loadコマンドでデータベースをリストア
docker run --rm --ulimit nofile=64000 \
-p 7474:7474 \
-p 7687:7687 \
-v ${PWD}/neo4j/data:/var/lib/neo4j/data \
-v ${PWD}/neo4j/import:/var/lib/neo4j/import \
-e NEO4J_dbms_memory_pagecache_size=6G \
-e NEO4J_dbms_memory_heap_max__size=6G \
neo4j bin/neo4j-admin load --from=./data/tpch-sf001.dump --database=graph.db --force
# あらためてneo4jコンテナを立ち上げる
docker run --ulimit nofile=64000 \
-p 7474:7474 \
-p 7687:7687 \
-v ${PWD}/neo4j/data:/var/lib/neo4j/data \
-v ${PWD}/neo4j/import:/var/lib/neo4j/import \
-e NEO4J_dbms_memory_pagecache_size=6G \
-e NEO4J_dbms_memory_heap_max__size=6G \
neo4j
ホストでneo4jを実行している場合
- neo4jサーバを止めてから,
neo4j-admin load
コマンドを実行するだけです
neo4j stop
neo4j-admin load --from=./data/tpch-sf001.dump --database=graph.db --force
neo4j start
- 次回はクエリを実施します