1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

GraphDBを学ぶ(4)neo4jにTPC-Hのテーブルを登録(+バックアップ・リストア)

Last updated at Posted at 2019-04-26

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のテーブル間の関連を確認します
    • 矢印がhas-manyの関連です
    • テーブル間の文字列は,関係を表す名前(自由に決められるもの)です

tpch.png

グラフノードの登録

  • 以下の手順でNeo4jにグラフを作ります
  1. MariaDBからテーブルのcsvファイルを作成する
  2. Neo4jに,テーブル=ラベル,行=ノードとして,外部キー以外のエントリを書き込む
  3. 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データベースを登録することができました

tpch-graph.jpg

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/datatpch-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
  • 次回はクエリを実施します

参考

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?