写点什么

neo4j 批量 导入 数据 的 几种方式

用户头像
wkq2786130
关注
发布于: 2020 年 07 月 22 日



原文 http://weikeqin.com/2017/04/14/neo4j-import-data/



Neo4j是一个比较新的数据库,ETL工具较少,公司一个项目需要导入上百亿数据,想找一个最合适的方案来导入数据。

于是就想测测各种导入方式的效率以及成本



常见数据插入方式概览



目前主要有以下几种数据插入方式:

  1. Cypher create 语句,为每一条数据写一个create

  2. Cypher load csv 语句,将数据转成CSV格式,通过LOAD CSV读取数据。

  3. 官方提供的neo4j-import工具,未来将被[neo4j-admin import](https://neo4j.com/docs/operations-manual/current/tools/import/)代替

  4. 官方提供的Java API - BatchInserter

  5. 大牛编写的 batch-import 工具

  6. neo4j-apoc  load.csv + apoc.load.relationship

  7. 针对实际业务场景,定制化开发



这些工具有什么不同呢?速度如何?适用的场景分别是什么?



各种导入数据方法测试



(1) create语句



个人认为,只有在学习neo4j的时候会用到create,在批量导入数据的时候,一般很少用create

未测

./neo4j-shell -c < /data/stale/data01/neo4j/neo4j_script.cypther



./bin/neo4j-shell -path ./data/databases/graph.db -conf ./conf/neo4j.conf -file create_index.cypther



(2) load csv 语句



服务器配置 CPU 32核,256G内存,10T机械硬盘



数据格式

uuid,name,Label
b6b0ea842890425588d4d3cfb38139a9,"文烁",Label1
5099c4f943d94fa1873165e3f6f3c2fb,"齐贺喜",Label3
c83ed0ae9fb34baa956a42ecf99c8f6e,"李雄",Label2
e62d1142937f4de994854fa1b3f0670a,"房玄龄",Label



下面是详细测试结果



(2.1) 导入10w数据(仅节点)

neo4j-sh (?)$ using periodic commit 10000 load csv with headers from "file:/data/stale/data01/neo4j/node_uuid_10w.csv" as line with line create (:Test {uuid:line.uuid, name:line.name});
+-------------------+
| No data returned. |
+-------------------+
Nodes created: 100000
Properties set: 200000
Labels added: 100000
3412 ms

10万数据(只有节点,没有关系)导入用时3.412s



(2.2) 导入1kw数据(仅节点)



/data/stale/data01/neo4j/nodeuuid1kw.csv文件加上标题一共10000010条,有10000009条数据



neo4j-sh (?)$ load csv from "file:/data/stale/data01/neo4j/node_uuid_1kw.csv" as line return count(*);
+----------+
| count(*) |
+----------+
| 10000010 |
+----------+
1 row
7434 ms
neo4j-sh (?)$ using periodic commit 10000 load csv with headers from "file:/data/stale/data01/neo4j/node_uuid_1kw.csv" as line with line create (:Test {uuid:line.uuid, name:line.name});
+-------------------+
| No data returned. |
+-------------------+
Nodes created: 10000009
Properties set: 20000018
Labels added: 10000009
151498 ms

1千万数据(只有节点,没有关系)导入用时151.498s

导入时CPU利用率在150%左右,RES Memory 5G左右,VIRT Memory 70G左右



(2.3) 导入100w数据(仅关系)

neo4j-sh (?)$ match (n) return count(n);
+----------+
| count(n) |
+----------+
| 18004002 |
+----------+
1 row
12 ms
neo4j-sh (?)$ match ()-->() return count(*);
+----------+
| count(*) |
+----------+
| 15001999 |
+----------+
1 row
18 ms
neo4j-sh (?)$ using periodic commit 100000 load csv with headers from "file:/data/stale/data01/neo4j/relathionship_uuid_100w.csv" as line with line merge (n1:Test {uuid:line.uuid1}) merge (n2:Test {uuid:line.uuid2}) with * create (n1)-[r:Relationship]->(n2);
+-------------------+
| No data returned. |
+-------------------+
Relationships created: 1000000
75737 ms
neo4j-sh (?)$ match (n) return count(n);
+----------+
| count(n) |
+----------+
| 18004002 |
+----------+
1 row
6 ms
neo4j-sh (?)$ match ()-->() return count(*);
+----------+
| count(*) |
+----------+
| 16001999 |
+----------+

创建100w关系用时75.737s

因为我节点已经提前导入了,所以merge的时候节点全部存在,根据结果可以看到,只创建了100w关系,没有创建节点

但是这种方式有一个弊端,关系要写死,在只有一种关系时试用,在有多种关系时,不适用。还有一个不好的地方就是用的merge,uuid是String类型,会随着数据的正常速度变慢。



load csv 的速度我用的是 导入节点时间+导入关系时间

导入100w 节点+数据 (Label写死,RelationShip写死,也就是只有一种Label和一种RelationShip) 共花费15.149 + 75.737 = 90.886 。load csv的速度大概在1.1w/s,但这种情况一般很少使用,仅供参考。



(3) neo4j-import (在以后版本会被neo4j-admin import替掉)



服务器配置 CPU 32核,256G内存,10T机械硬盘

空库初始化导入1千万数据(1kw节点 1kw关系 2kw属性,id使用integer,属性中只有数字和英文)花费27s 932ms

空库初始化导入1千万数据(1kw节点 1kw关系 2kw属性,包含中文属性)花费1min 50s 9ms

空库初始化导入1.1亿数据(1.1亿节点 1.1关系 2.2亿属性)花费15min 9s 37ms



数据格式



node.csv

uuid:ID(users),name:String,:Label
c63bc1e7dc594fd49fbe36dd664ff0a6,"维特",Label1
b52fb5f2266b4edbadc82b5ec4c430b8,"廖二松",Label2
d95d430cfeee47dd95f9bf5e0ec1ae93,"徐青偏",Label3
b2d1fffc8173461fa603d4fbb601b3ee,"杨础维",Label2



relationship.csv

uuid:START_ID(users),uuid:END_ID(users),:TYPE
c63bc1e7dc594fd49fbe36dd664ff0a6,b2d1fffc8173461fa603d4fbb601b3ee,RelationShip1
d95d430cfeee47dd95f9bf5e0ec1ae93,c63bc1e7dc594fd49fbe36dd664ff0a6,RelationShip2
b2d1fffc8173461fa603d4fbb601b3ee,b52fb5f2266b4edbadc82b5ec4c430b8,RelationShip3
b52fb5f2266b4edbadc82b5ec4c430b8,d95d430cfeee47dd95f9bf5e0ec1ae93,RelationShip1



部分详细日志如下



(3.1) 导入1000w数据(1kw节点 1kw关系 2kw属性,id使用integer,属性中只有数字和英文)

空库初始化导入1千万数据(1kw节点 1kw关系 2kw属性,id使用integer,属性中只有数字和英文)花费27s 932ms

部分日志如下:

Done in 779ms

IMPORT DONE in 27s 932ms.
Imported:
10000000 nodes
10000000 relationships
20000000 properties
Peak memory usage: 209.81 MB



(3.2) 导入1000w数据(1kw节点 1kw关系 2kw属性,包含中文属性)

空库初始化导入1千万数据(1kw节点 1kw关系 2kw属性,包含中文属性)花费1min 50s 9ms

部分日志如下:

IMPORT DONE in 1m 50s 9ms.
Imported:
10000000 nodes
10000000 relationships
20000000 properties
Peak memory usage: 209.81 MB



(3.3) 导入1.1亿数据(1.1亿节点 1.1关系 2.2亿属性)

空库初始化导入1.1亿数据(1.1亿节点 1.1关系 2.2亿属性)花费15min 9s 37ms

部分日志如下:

IMPORT DONE in 15m 9s 37ms.
Imported:
110000010 nodes
110000000 relationships
220000020 properties
Peak memory usage: 2.27 GB
There were bad entries which were skipped and logged into /data/stale/data01/neo4j/neo4j-community-3.1.0/data/databases/test_uuid_1y_graph.db/bad.log



(4) BatchInserter



batch-import调的BatchInserter的代码,所以BatchInserter没测,可以认为BatchInster和batch-import速度一样



(5) batch-import



在数据库中已有1kw数据的情况下,导入100w数据(100w节点 100w关系 200w属性,包含中文属性)花费92s



node.csv

uuid:string:users,name:String,:label #官方的文件头
uuid:ID(users),name:String,:Label #修改程序后的文件头
c63bc1e7dc594fd49fbe36dd664ff0a6,"维特",Label1
b52fb5f2266b4edbadc82b5ec4c430b8,"廖二松",Label2
d95d430cfeee47dd95f9bf5e0ec1ae93,"徐青偏",Label3
b2d1fffc8173461fa603d4fbb601b3ee,"杨础维",Label2



relationship.csv

uuid:string:users,uuid:string:users,type #官方的文件头
uuid:START_ID(users),uuid:END_ID(users),:TYPE #修改程序后的文件头
c63bc1e7dc594fd49fbe36dd664ff0a6,b2d1fffc8173461fa603d4fbb601b3ee,RelationShip1
d95d430cfeee47dd95f9bf5e0ec1ae93,c63bc1e7dc594fd49fbe36dd664ff0a6,RelationShip2
b2d1fffc8173461fa603d4fbb601b3ee,b52fb5f2266b4edbadc82b5ec4c430b8,RelationShip3
b52fb5f2266b4edbadc82b5ec4c430b8,d95d430cfeee47dd95f9bf5e0ec1ae93,RelationShip1



Using: Importer batch.properties /data/stale/data01/neo4j/neo4j-community-3.1.0/data/databases/test_uuid_1000w_graph.db /data/stale/data01/neo4j/node_uuid_100w.csv /data/stale/data01/neo4j/relationship_uuid_100w.csv

Using Existing Configuration File
..........
Importing 1000000 Nodes took 15 seconds
..........
Importing 1000000 Relationships took 16 seconds

Total import time: 92 seconds



(6) apoc



load csv + merge + apoc.create.relationship



neo4j-sh (?)$ using periodic commit 1000000
> load csv from 'file:/data/stale/data01/neo4j/relathionship_uuid_1kw.csv' as line fieldterminator ','
> merge (n1:Test {uuid: line[0]})
> merge (n2:Test {uuid: line[1]})
> with n1, n2, line
> CALL apoc.create.relationship(n1, line[2], {}, n2) YIELD rel
> return count(rel) ;
+------------+
| count(rel) |
+------------+
| 10000010 |
+------------+
1 row
Nodes created: 8645143
Properties set: 8645143
Labels added: 8645143
2395852 ms



在1.1亿数据上增量更新1kw数据花费2395.852s

VIRT Memory 90G RES Memory 78G



结论



根据实际情况选用最好的方式



  1. neo4j-import导入速度快,但是要求是空库,导入时要停止neo4j,也就是脱机导入,而且你要提前处理好数据,数据最好不要有重复,如果有重复,可以导入时跳过,然后根据bad.log来查看或者修正这部分数据

  2. batch-import可以增量导入,但是要求导入时停止neo4j数据库(脱机导入),而且增量更新的数据不会和库里存在的数据对比,所以要求数据全是新的,否则会出现重复数据

  3. load csv比较通用,而且可以在neo4j数据库运行时导入,但是导入速度相对较慢,要提前整理好数据,而且不能动态创建 Label RelationShip

  4. apoc挺好用的,可以动态创建RelationShip,但是不能动态创建Label (动态创建Label只能在程序里通过拼接字符串的方法实现)



实际情况中,处理数据比导入数据更花费时间



Neo4j的查询速度为何这么慢?这能商用吗? 这篇文章中 HackerWhite 写的那部分特别好,可以参考。



References

[1] guide-importing-data-and-etl

[2] guide-import-csv

[3] load-csv

[4] import

[5] how-to-insert-bulk-data-into-neo4j

[6] Neo4j的查询速度为何这么慢?这能商用吗?

[7] 使用batch-import工具向neo4j中导入海量数据

[8] 如何快速导入网络数据到图数据库Neo4j

[9] bulk-data-import-neo4j-3-0

[10] neo4j-etl github

[11] neo4j google group

[12] import-tool

[13] Neo4j在并发的load CSV文件的时候出现deadlock问题。怎么有效处理提高并发呢?



发布于: 2020 年 07 月 22 日阅读数: 103
用户头像

wkq2786130

关注

hello 2018.09.28 加入

http://weikeqin.com/

评论

发布
暂无评论
neo4j 批量 导入 数据 的 几种方式