孙宇的技术专栏 大数据/机器学习

图数据库 neo4j 使用

2019-01-21

阅读:


介绍

  1. 节点,关系和属性中的数据
  2. 节点和关系都包含属性
  3. 关系连接节点
  4. 属性是键值对
  5. 节点用圆圈表示,关系用方向键表示。
  6. 关系具有方向:单向和双向。
  7. 每个关系包含“开始节点”或“从节点”和“到节点”或“结束节点”

在属性图数据模型中,关系应该是定向的。如果我们尝试创建没有方向的关系,那么它将抛出一个错误消息。

Neo4j图数据库将其所有数据存储在节点和关系中。

主要构建块是:

  1. 节点。图表的基本单位。 它包含具有键值对的属性
  2. 关系。它连接两个节点。每个关系包含一个起始节点和一个结束节点。
  3. 属性。描述图节点和关系的键值对
  4. 标签。将一个公共名称与一组节点或关系相关联。 节点或关系可以包含一个或多个标签。

CQL

命令介绍 —

CQL命令 介绍
CREATE 创建节点,关系和属性
MATCH 检索有关节点,关系和属性数据
RETURN 返回查询结果
WHERE 条件过滤检索数据
DELETE 删除节点和关系
REMOVE 删除节点和关系的属性
ORDER BY 排序检索数据
SET 添加或更新标签

CREATE

创建没有属性的节点

CREATE (<node-name>:<label-name>)

CREATE (emp:Employee)

emp是一个节点名。Employee是emp节点的标签名称。

创建带属性的节点

CREATE (
   <node-name>:<label-name>
   { 	
      <Property1-name>:<Property1-Value>
      ........
      <Propertyn-name>:<Propertyn-Value>
   }
)

CREATE (dept:Dept { deptno:10,dname:"Accounting",location:"Hyderabad" })

创建不带属性的关系

CREATE  
   (<node1-label-name>:<node1-name>)-
   [<relationship-label-name>:<relationship-name>]->
   (<node1-label-name>:<node1-name>)
RETURN <relationship-label-name>

CREATE (fb1:FaceBookProfile1)-[like:LIKES]->(fb2:FaceBookProfile2) 

MATCH (fb1:FaceBookProfile1)-[like:LIKES]->(fb2:FaceBookProfile2) 
RETURN like

创建带属性的关系

CREATE  
	(<node1-label-name>:<node1-name>{<define-properties-list>})-
	[<relationship-label-name>:<relationship-name>{<define-properties-list>}]
	->(<node1-label-name>:<node1-name>{<define-properties-list>})
RETURN <relationship-label-name>

CREATE (video1:YoutubeVideo1{title:"Action Movie1",updated_by:"Abc",uploaded_date:"10/10/2010"})
-[movie:ACTION_MOVIES{rating:1}]->
(video2:YoutubeVideo2{title:"Action Movie2",updated_by:"Xyz",uploaded_date:"12/12/2012"}) 

MATCH (video1:YoutubeVideo1)-[movie:ACTION_MOVIES]->(video2:YoutubeVideo2) 
RETURN movie

节点之间创建不带属性的关系

MATCH (e:Customer),(cc:CreditCard) 
CREATE (e)-[r:DO_SHOPPING_WITH ]->(cc) 

这里关系名称为 DO_SHOPPING_WITH

关系标签为 r

e 和 Customer 分别是客户节点的 节点名称 和 节点标签。

cc 和 CreditCard 分别是 CreditCard 节点的 节点名 和 节点标签

还可以用相反的方式创建 cc 到 e 的关系。这样就得到一个双向关系。

节点之间创建带属性的关系

MATCH (cust:Customer),(cc:CreditCard) 
CREATE (cust)-[r:DO_SHOPPING_WITH{shopdate:"12/12/2014",price:55000}]->(cc) 
RETURN r

关系的方向

节点之间的关系是有方向性的。 它们是单向或双向的。如果我们尝试创建一个没有任何方向的关系, Neo4j DB服务器会抛出一个错误。() - []→() 表明了关系的方向。从一个节点到另一个节点。

为节点创建单个或多个标签

CREATE (<node-name>:<label-name>)

CREATE (google1:GooglePlusProfile)

CREATE (m:Movie:Cinema:Film:Picture)

Movie, Cinema, Film, Picture是m节点的多个标签名称

为关系创建单个或多个标签

CREATE (<node1-name>:<label1-name>)-
	[(<relationship-name>:<relationship-label-name>)]
	->(<node2-name>:<label2-name>)
	
CREATE (p1:Profile1)-[r1:LIKES]->(p2:Profile2)

p1 和 profile1 是节点名称和节点标签名称 From Node

p2 和 Profile2 是 To Node 的节点名称和节点标签名称

r1 是关系名称

LIKES 是一个关系标签名称

MATCH

从数据库获取有关节点和属性的数据

MATCH 
(
   <node-name>:<label-name>
)

MATCH (dept:Dept)

RETURN

RETURN 
   <node-name>.<property1-name>,
   ........
   <node-name>.<propertyn-name>
   
RETURN dept.deptno

RETURN + MATCH

MATCH Command
RETURN Command

MATCH (dept: Dept)
RETURN dept.deptno, dept.dname

CREATE + MATCH + RETURN

CREATE (e:Customer{id:"1001",name:"Abc",dob:"01/10/1982"})
CREATE (cc:CreditCard{id:"5001",number:"1234567890",cvv:"888",expiredate:"20/17"})

MATCH (e:Customer)
RETURN e.id, e.name, e.dob

MATCH (cc:CreditCard)
RETURN cc.id, cc.number, cc.cvv, cc.expiredate

查询关系

MATCH (e)-[r:DO_SHOPPING_WITH ]->(cc) 
RETURN r

MATCH 
(<node1-label-name>)-[<relationship-label-name>:<relationship-name>]->(<node2-label-name>)
RETURN <relationship-label-name>

根据属性匹配节点信息

MATCH (n:Person{name:"李四"}}) RETURN  n

字符串匹配

匹配字符串的开头

MATCH (n)
WHERE n.name STARTS WITH '张'
RETURN n

匹配字符串的末尾

MATCH (n)
WHERE n.name ENDS WITH '三'
RETURN n

匹配字符串的包含匹配

MATCH (n)
WHERE n.name CONTAINS '三'
RETURN n

字符串排除匹配

使用NOT关键字来排除匹配到的结果,得到相反的结果

MATCH (n)
WHERE NOT n.name STARTS WITH '张'
RETURN n

字符串正则表达式

模糊匹配,类似sql语句中的like

MATCH (n)
WHERE n.name =~ '.*小.*'
RETURN n

不区分大小写正则匹配

MATCH (n)
WHERE n.name =~ '(?i)ANDR.*'
RETURN n

路径匹配

逻辑运算符号和路径作为过滤条件

匹配名称为”李四”或”王五”且与”张三”有任何关系的节点,并返回符合匹配条件的节点信息

MATCH (n { name: '张三' }),(m)
WHERE m.name IN ['李四', '王五'] AND (n)<--(m)
RETURN m

NOT逻辑运算符号

匹配和张三没有关系的人

MATCH (persons),(zhangsan { name: '张三' })
WHERE NOT (persons)-->(zhangsan)
RETURN persons

关系深度匹配

任意关系,深度1到5的节点

MATCH p=(n)-[*1..5]->(m) RETURN p

任意关系、任意深度的节点

MATCH p=(n)-[*]->(m) RETURN p

WITH

将第一部分匹配的输出作为下一部分匹配的输入

查找有十个以上朋友的张姓男子

MATCH (user)-[:FRIEND]-(friend)
WHERE user.name =~ '张.*'
WITH user, count(friend) AS friends
WHERE friends > 10
RETURN user

使用ORDER BY、SKIP 和 LIMIT 语句

MATCH (user)-[:FRIEND]-(friend)
WITH user, count(friend) AS friends
ORDER BY friends DESC
SKIP 1
LIMIT 3
RETURN user

WHERE

WHERE <condition>
WHERE <condition> <boolean-operator> <condition>

boolean-operator 布尔运算包括:

AND, OR, NOT, XOR

condition 语法

<property-name> <comparison-operator> <value>

comparison-operator 操作运算符包括:

=, <, >, <=, >=

如:

MATCH (emp:Employee) 
WHERE emp.name = 'Abc'
RETURN emp

MATCH (emp:Employee) 
WHERE emp.name = 'Abc' OR emp.name = 'Xyz'
RETURN emp

为 where 子句的结果创建关系

MATCH (cust:Customer),(cc:CreditCard) 
WHERE cust.id = "1001" AND cc.id= "5001" 
CREATE (cust)-[r:DO_SHOPPING_WITH{shopdate:"12/12/2014",price:55000}]->(cc) 
RETURN r

DELETE

删除节点及相关节点和关系

MATCH (e: Employee)
DELETE e

MATCH (cc: CreditCard)-[rel]-(c:Customer) 
DELETE cc, c, rel

REMOVE

删除节点或关系的现有属性。包括标签

CREATE (book:Book {id:122,title:"Neo4j Tutorial",pages:340,price:250}) 

MATCH (book { id:122 })
REMOVE book.price
RETURN book

删除标签:

MATCH (m:Movie) 
REMOVE m:Picture

SET

向现有节点或关系添加新属性

MATCH (dc:DebitCard)
SET dc.atm_pin = 3456
RETURN dc

ORDER BY排序

MATCH (emp:Employee)
RETURN emp.empid,emp.name,emp.salary,emp.deptno
ORDER BY emp.name DESC

Union合并

将两个不同的结果合并成一组结果

<MATCH Command1>
   UNION
<MATCH Command2>

MATCH (cc:CreditCard) RETURN cc.id,cc.number
UNION
MATCH (dc:DebitCard) RETURN dc.id,dc.number

MATCH (cc:CreditCard)
RETURN cc.id as id,cc.number as number,cc.name as name,
   cc.valid_from as valid_from,cc.valid_to as valid_to
UNION
MATCH (dc:DebitCard)
RETURN dc.id as id,dc.number as number,dc.name as name,
   dc.valid_from as valid_from,dc.valid_to as valid_to

UNION ALL

结合并返回两个结果集的所有行成一个单一的结果集。但:两个结果集的名字必须匹配,列名称应该是相同的,列的数据类型也应该是相同

MATCH (cc:CreditCard)
RETURN cc.id as id,cc.number as number,cc.name as name,
   cc.valid_from as valid_from,cc.valid_to as valid_to
UNION ALL
MATCH (dc:DebitCard)
RETURN dc.id as id,dc.number as number,dc.name as name,
   dc.valid_from as valid_from,dc.valid_to as valid_to

LIMIT 和 SKIP

过滤或限制查询返回的行数。Limit 返回结果集的前 N 行,Skip 返回最后的 N 行。

MATCH (emp:Employee) 
RETURN emp
LIMIT 2

MATCH (emp:Employee) 
RETURN emp
SKIP 2

Null 值

CQL 将 空值 视为对节点或关系的属性的默认值。当我们创建一个只有标签名称但没有属性的节点时,它将创建一个具有 NULL 属性值的新节点。

CREATE (e:Employee)

MATCH (e:Employee) 
RETURN e.id,e.name,e.sal,e.deptno

这里创建了新节点,没有添加任何属性。但后面查询了它的属性,这时候返回的值就是 NULL。可以进行过滤:

CREATE (e:Employee)

MATCH (e:Employee) 
WHERE e.id IS NOT NULL
RETURN e.id,e.name,e.sal,e.deptno

IN 查找

MATCH (e:Employee) 
WHERE e.id IN [123,124]
RETURN e.id,e.name,e.sal,e.deptno

ID属性

Id 是节点和关系的默认内部属性。 这意味着,当我们创建一个新的节点或关系时,Neo4j数据库服务器将为内部使用分配一个数字。 它会自动递增。最大值约为 35 亿。

CREATE (tweet:Tweet{message:"Hello"})

MATCH (tweet:Tweet{message:"Hello"})
RETURN tweet

如果再添加,可以在图形界面看到节点属性里 ID 的递增。

建索引

CREATE INDEX ON :<label_name> (<property_name>)

注意:

冒号(:)运算符用于引用节点或关系标签名称。

上述语法描述它在节点或关系的的上创建一个新索引。

如:

CREATE INDEX ON :Customer (name)

删除索引

DROP INDEX ON :Customer (name)

函数介绍

CQL函数 介绍
String 使用String字面量
Aggregation 对CQL查询结果执行一些聚合操作
Relationship 获取关系的细节,如startnode,endnode等

字符串函数

CQL提供了一组String函数,用于在CQL查询中获取所需的结果。如:UPPER, LOWER, SUBSTRING, REPLACE。

MATCH (e:Employee) 
RETURN e.id, UPPER(e.name), SUBSTRING(e.name, 0, 2) as short_name, e.sal, e.deptno, REPLACE(e.name, 'aaa', 'bbb') as replaced_name, 

AGGREGATION 聚合

可以用来查询结果中的诸如:COUNT, MAX, MIN, SUM, AVG 数据。

MATCH (e:Employee) 
RETURN MAX(e.sal) as max_sal, MIN(e.sal) as min_sal, AVG(e.sal) as avg_sal, SUM(e.sal) as total_sal, COUNT(*) as cnt

关系函数

获取开始节点,结束节点等细节时知道关系的细节: STARTNODE: 关系的开始节点; ENDNODE: 关系的结束节点; ID; TYPE

STARTNODE (<relationship-label-name>)

MATCH (video1:YoutubeVideo1)-[movie:ACTION_MOVIES]->(video2:YoutubeVideo2) 
RETURN STARTNODE (movie) as start_node, ENDNODE (movie) as end_node, ID(movie), TYPE(movie)

数据类型

CQL数据类型 介绍
boolean 用于表示布尔文字:true,false。
byte 8位整数
short 16位整数
int 32位整数
long 64位整数
float 32位浮点数
double 64位浮点数
char 16位字符
String 字符串

管理

索引

节点或关系属性上可以添加索引,以提高应用程序的性能。可以在MATCH或WHERE或IN运算符上使用这些索引列来改进CQL

CREATE INDEX ON :<label_name> (<property_name>)

CREATE INDEX ON :Customer (name)

DROP INDEX ON :Customer (name)

UNIQUE

CQL CREATE 命令始终创建新的节点或关系.

这意味着即使使用相同的值,它也会插入一个新行。 有时,我们想避免这种重复,可以使用一些数据库约束来创建节点或关系的一个或多个属性的规则: UNIQUE

CREATE CONSTRAINT ON (<label_name>)
ASSERT <property_name> IS UNIQUE

CREATE CONSTRAINT ON (cc:CreditCard)
ASSERT cc.number IS UNIQUE

如果当前已经有重复的数据,创建唯一约束时会失败,这时候需要先删除重复的数据再创建。

MATCH (cc:CreditCard) 
WHERE cc.number = 222222
DELETE cc

创建了唯一约束后,如果再添加重复的数据,会失败。

删除唯一约束的方式:

DROP CONSTRAINT ON (<label_name>)
ASSERT <property_name> IS UNIQUE

DROP CONSTRAINT ON (cc:CreditCard)
ASSERT cc.number IS UNIQUE

数据导入

通过API,我们可以逐条将节点和关系创建。但如果有上万,甚至百万千万以及更多数据时。如果一条条处理,可以想象性能是非常差的。类似 MySQL ,当有大量数据时, load data 会有数量级的性能提升。同理,neo4j 也提供了类似的功能甚至是更多的方式。

数据导入,可选的方式有:

  1. create 语句,为每一条数据写一个create
  2. load csv 语句,将数据转成CSV格式,通过LOAD CSV读取数据
  3. 官方提供的neo4j-import工具,未来将被neo4j-adminimport代替
  4. 官方提供的Java API: BatchInserter
  5. 大牛编写的 batch-import 工具
  6. neo4j-apocload.csv +apoc.load.relationship

经网友验证,性能大致对比如下:

  create 语句 load csv neo4j-import BatchInster batch-import apoc
适用场景 小型数据 初始化导入
增量更新
初始化导入 初始化导入 初始化导入
增量更新
增量更新
导入速度 很慢1000/s 数 k /s 数 w /s 数 w /s 数 w /s 数 k /s
实际测试 9.5k/s 12w/s 1w/s 1w/s 4k/s[1亿数据上增量]
1w/s[百万数据上增量]
优点 使用方便
实时插入
官方工具
可加载本地和远程CSV
实时插入
官方工具
占用资源少
官方 API 增量更新
基于 BatchInserter
官方工具
增量更新
可在线导入
缺点 速度慢
拼CQL复杂
速度较慢
不能动态传关系
要停数据库
要在JAVA环境中使用
要停止数据库
不能增量,只能初始化或覆盖
要停数据库 速度一般

load csv

简单导入

artists.csv

1,ABBA,1992
2,Roxette,1986
3,Europe,1979
4,The Cardigans,1992

导入:

LOAD CSV FROM 'https://neo4j.com/docs/cypher-manual/3.5/csv/artists.csv' AS line
CREATE (:Artist { name: line[1], year: toInteger(line[2])})

+-------------------+
| No data returned. |
+-------------------+
Nodes created: 4
Properties set: 8
Labels added: 4

导入包含头部的CSV

artists-with-headers.csv

Id,Name,Year
1,ABBA,1992
2,Roxette,1986
3,Europe,1979
4,The Cardigans,1992

导入:

LOAD CSV WITH HEADERS FROM 'https://neo4j.com/docs/cypher-manual/3.5/csv/artists-with-headers.csv' AS line
CREATE (:Artist { name: line.Name, year: toInteger(line.Year)})

+-------------------+
| No data returned. |
+-------------------+
Nodes created: 4
Properties set: 8
Labels added: 4

导入自定义分割符的CSV

artists-fieldterminator.csv

1;ABBA;1992
2;Roxette;1986
3;Europe;1979
4;The Cardigans;1992

导入:

LOAD CSV FROM 'https://neo4j.com/docs/cypher-manual/3.5/csv/artists-fieldterminator.csv' AS line FIELDTERMINATOR ';'
CREATE (:Artist { name: line[1], year: toInteger(line[2])})

+-------------------+
| No data returned. |
+-------------------+
Nodes created: 4
Properties set: 8
Labels added: 4

导入大量数据

如果数据量非常大,可以使用 USING PERIODIC COMMIT,批量提交。默认会一次提交 1000 行。如:

USING PERIODIC COMMIT
LOAD CSV FROM 'https://neo4j.com/docs/cypher-manual/3.5/csv/artists.csv' AS line
CREATE (:Artist { name: line[1], year: toInteger(line[2])})

也可以自定义一次提交的行数:

USING PERIODIC COMMIT 500
LOAD CSV FROM 'https://neo4j.com/docs/cypher-manual/3.5/csv/artists.csv' AS line
CREATE (:Artist { name: line[1], year: toInteger(line[2])})

导入包含转义字符的数据

artists-with-escaped-char.csv

"1","The ""Symbol""","1992"

导入:

LOAD CSV FROM 'https://neo4j.com/docs/cypher-manual/3.5/csv/artists-with-escaped-char.csv' AS line
CREATE (a:Artist { name: line[1], year: toInteger(line[2])})
RETURN a.name AS name, a.year AS year, size(a.name) AS size

+------------------------------+
| name           | year | size |
+------------------------------+
| "The "Symbol"" | 1992 | 12   |
+------------------------------+
1 row
Nodes created: 1
Properties set: 2
Labels added: 1

从 name 的 size 可以看出,引号是被算成名称的一部分的。

测试导入

load csv 不能动态传Label、RelationShip,所以测试时Label、RelationShip是写死的

数据如下:

31001986900050040272_1,上*思盾网络技术有限公司,31001986900050040272_1,100002,1,Main
8110201012800355973,上*翘佑投资管理有限公司,8110201012800355973,100002,1,Main
37940188000027492,山*赢任信息技术有限公司,37940188000027492,100002,1,Main
310069053018170148591,上*学清投资有限公司,310069053018170148591,100002,1,Main
310066344018010032559,上*捷申规划建筑设计工程有限公司,310066344018010032559,100002,1,Main
22810401040015380,成*斐讯科技有限公司,,100002,1,Main

导入10w节点

using periodic commit 1000 LOAD CSV FROM 'file:/User.csv' AS line
CREATE (:User { card: line[0], name: line[1], account: line[2], case: line[3], batch: line[4], source: line[5]})

+-------------------+
| No data returned. |
+-------------------+
Nodes created: 100000
Properties set: 500000
Labels added: 100000
3412 ms

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

导入1kw节点

load csv from "file:/node_uuid_1kw.csv" as line return count(*);
+----------+
| count(*) |
+----------+
| 10000000 |
+----------+
1 row
7434 ms

using periodic commit 1000 load csv from "file:/node_uuid_1kw.csv" as line with line create (:Test {uuid:line.uuid, name:line.name});
+-------------------+
| No data returned. |
+-------------------+
Nodes created: 10000000
Properties set: 50000000
Labels added: 10000000
151498 ms

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

导入100w关系

关系数据如:

35050188000700000572,414358360398,2018-06-14 18:25:42,3216,CNY,164314590,100002,1
00000397461552722,414358360398,2018-05-10 12:41:11,500000,CNY,109361082,100002,1
35050188000700000572,414358360398,2018-05-10 13:11:47,500000,CNY,113810907,100002,1
591157492615,414358360398,2017-12-01 10:36:02,40318.68,CNY,78089892,100002,1
6217886400000221882,414358360398,2017-12-01 11:47:45,38620.2,CNY,88287514,100002,1
591157492615,414358360398,2017-12-04 11:26:21,1,CNY,84487993,100002,1
591157492615,414358360398,2017-12-04 11:26:28,102042.95,CNY,84506860,100002,1
591157492615,414358360398,2017-12-04 11:26:29,67417.14,CNY,84508985,100002,1
591157492615,414358360398,2017-12-04 11:26:31,98653.15,CNY,84512839,100002,1

导入:

using periodic commit 200 load csv from "file:/Transfer.csv" as line
merge (n1:User {card:line[0]})
with n1, line
merge (n2:User {card:line[1]})
create (n1)-[:Transfer{from: line[0], target: line[1], time: line[2], money: toFloat(line[3]), cash: line[4], flow: line[5], case: line[6], batch: line[7]}]->(n2);

+-------------------+
| No data returned. |
+-------------------+
Relationships created: 1000000
75737 ms

创建100w关系用时75.737s。因为节点已经提前导入了,所以merge的时候节点全部存在,只创建了100w关系,没有创建节点。但是这种方式有一个弊端,关系要写死,在只有一种关系时适用,在有多种关系时,不适用。

还有一个不好的地方就是用的merge,uuid是String类型,会随着数据的增长速度变慢。

附: 一些查询命令:

全景图:

MATCH (n1:User)-[r:Transfer]->(n2:User) RETURN n1,r,n2

查主帐号间转帐信息:

MATCH (n1:User)-[r:Transfer]->(n2:User) 
where n1.source="Main" and n2.source="Main"  
RETURN n1,r,n2

查两个帐户之间的链路:

match p=(n:User {card:'37940188000027492'})-[:Transfer*2]->(m:User {card:'310069053018170148591'}) 
return distinct(p),m,n

neo4j-import

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

导入1000w数据

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

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

BatchInster

可以认为BatchInster和batch-import速度一样

batch-import

node.csv

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

relationship.csv

uuid:string:users,uuid:string: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

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

apoc 导入

直接导入:

CALL apoc.load.csv('test_user_header.csv') yield map
with map
merge (:User{ca_id: map.ca_id, card: map.card, acct_id: map.acct_id, acct_name: map.acct_name, is_main: map.is_main})

批次导入:

CALL apoc.periodic.iterate('CALL apoc.load.csv("4959caf0e8864caca1e88330d7ce1892User.csv") yield map as row return row', 'CREATE (u:User) SET u = row', {batchSize:10000, iterateList:true, parallel:true});
CREATE INDEX ON :User (ca_id,card)

CALL apoc.periodic.iterate('CALL apoc.load.csv("4003dccd9d2b46ab96e7cda939d2f6a3TransferIn.csv" , {sep:"\t", header:TRUE}) yield map as row ',
'match (n:User) where n.ca_id = "1" and n.card = row.from with n, row
match (m:User) where m.ca_id = "1" and m.card = row.target
create (n)-[r:Transfer]->(m) set r = row, r.any_id = "3"',
{batch:10000, iterateList:true, parallel:false})

这里。使用了 apoc 包,要先下载该插件,放到 neo4j 安装目录下的 plugins 目录下。

两个CSV文件都是带表头的。内容如:

acct_id	card	acct_name	is_main	ca_id
2052	00000000	招商银行信用卡中心	0	2
2053	000000000000000	中国建设银行	0	2
2054	0000000000000000	代付	0	2
2055	00000000000000000	财付通支付科技有限公司客户备付金	0	2
2056	0000000000000000000	0000000000000000000	0	2
2057	0000000000000000000000	0000000000000000000000	0	2
2058	00000000000000000000000000	00000000000000000000000000	0	2
2059	00000000000000000000000000000000	00000000000000000000000000000000	0	2
2060	0000000000100608134	苏宁消费金融有限公司	0	2
2061	0000000000174892275	苏宁金融服务上海有限公司南京分公司	0	2
from	target	found	time	money	ca_id
6222021602002353789	6212261602016903603	1	2016-10-12 10:57:06	0.01	2
1001278619005510123	6212261602016903603	1	2017-06-04 05:39:45	200000.00	2
1001278619005510123	6212261602016903603	1	2017-06-04 05:39:45	150000.00	2
1001278619005510123	6212261602016903603	1	2016-10-18 10:59:41	200000.00	2
1001278619005510123	6212261602016903603	1	2016-10-19 03:36:34	200000.00	2
1001278619005510123	6212261602016903603	1	2016-10-19 11:08:38	200000.00	2
1001278619005510123	6212261602016903603	1	2016-10-20 17:53:45	200000.00	2
1001278619005510123	6212261602016903603	1	2017-06-05 00:24:56	200000.00	2
1001278619005510123	6212261602016903603	1	2017-06-05 03:32:00	200000.00	2

load jdbc

除了从 csv 文件导入外,还可以直接从数据库导入。

CALL apoc.periodic.iterate('
call apoc.load.jdbc("jdbc:mysql://localhost:3306/northwind?user=root","company")',
'CREATE (p:Person) SET p += value', {batchSize:10000, parallel:true})
RETURN batches, total

结论

  1. neo4j-import导入速度快,但是要求是空库,导入时要停止neo4j,也就是脱机导入,而且你要提前处理好数据,数据最好不要有重复,如果有重复,可以导入时跳过,然后根据bad.log来查看或者修正这部分数据
  2. batch-import可以增量导入,但是要求导入时停止neo4j数据库(脱机导入),而且增量更新的数据不会和库里存在的数据对比,所以要求数据全是新的,否则会出现重复数据
  3. load csv比较通用,而且可以在neo4j数据库运行时导入,但是导入速度相对较慢,要提前整理好数据,而且不能动态创建 Label RelationShip
  4. apoc挺好用的,可以动态创建Label、RelationShip,但是速度一般

小型数据时,用 load csv 即可。

性能优化

数据需预热

使用bin/neo4j-shell 进入neo4j命令行界面,执行以下语句预热

MATCH (n)
OPTIONAL MATCH (n)-[r]->()
RETURN count(n.name) + count(r);

索引问题

首先使用explain ,看是否使用了索引,如果没有要添加索引

explain match data=(na)-[r]->(nb:company{name:'ss'}) return data;

上一篇 Zabbix安装使用

评论

文章