0%

图数据库-neo4j

图数据库的相关应用实践模块,如:

概述

选型:

  • 不要 cypher:Python思维,不要引入新的技术栈

考虑:

  • RedisGraph:已终止生命周期

Neo4j

Neo4j:开源图形数据,使用 Java 开发,

  • Attention:Python 牛批,去除 Cypher 的语法
    • 都用 java 底层的数据库了,还在意 python 丢失的性能?
    • 实用为上
  • 图形数据库,又称图形数据库管理系统或 GDBMS,以图形结构的形式存储数据的数据库。
  • 但是它
  • 基本概念:
    • 节点:Node,实体
      • 类似于 sql 里的 row
    • 标签:Label:实体类型,对节点分组,每个节点或关系可包含多个标签,标签被索引以加快查找
      • 类似于 sql 里的表
      • 只能通过标签名称来访问节点详细信息
    • 关系:Relationship
      • 类似于 sql 里的 join
    • 属性:Property/attrs:节点与关系的属性,键值对
      • 类似于 sql 里的字段
      • 可对属性创建索引与约束
      • Key 为字符串,Value 为基本类型或基本类型数组
    • 数据浏览器
  • 说明:
    • csv 文件的三元组(实体、属性、值),实体与值都是节点,属性是关系。
      • 感觉不太对…
  • 特点:
    • 嵌入式
    • 基于磁盘
    • 具备完全的事务特性
    • Java 持久化引擎
    • Cypher 查询语言
    • 将结构化数据存储在网络 (从数学角度叫做图) 上而不是表中。
    • 图数据库
      • 支持图属性
      • 支持 ACID
      • 高可用性,高连通数据
      • 支持 320 亿的结点,320 亿的关系结点,640 亿的属性
    • Lucene 索引
      • 高性能:一秒内搜索亿级节点(但一般不建议搜索全图)
    • 应用场景
      • 搜索
      • 智能问答
      • 推荐系统
  • todo:OUT 倒计事项
    • py2neo 操作,大数量级下慢得一批
    • 启动起来,吃了我 4.9G 的内存
    • 大内存操作占用大内存后(5.3G),不怎么回收 (4.5G),需要重启 neo4j

工具

  • Neo4j Browser:命令驱动的客户端,基于 web 的 shell 环境。

    • Ctrl+Enter:执行命令
    • Ctrl+ up/down:查看上/下一条命令
    1
    2
    3
    4
    5
    :help
    :help keys # 查看快捷键

    :clear
    :history

Cypher

Cypher 是一个描述性的图形查询语言,允许不必编写图形结构的遍历代码对图形存储有表现力和效率的查询,和 SQL 很相似,Cypher 语言的关键字不区分大小写,但是属性值,标签,关系类型和变量是区分大小写的。

Cypher:CQL:Neo4j 的图形数据库查询语言

  • 特点:
    • 声明性模式匹配语言
    • 使用 patterns(模式)描述图数据
    • 类 sql 风格
    • 声明式:描述要找到什么,而不是如何找到它
  • 应用:
    • 表达式:对于值的操作,如数值操作、字符串操作、关系操作、链表操作、映射表操作、路径操作等
    • 图模式:就是出现在 Cypher 查询语句 MATCH 子句中查询模式。Cypher 定义了两种基本查询模式:点模式和关系模式。所谓点模式是一个三元组 (a,L,P),其中 a 为所查询点对应的变量名、L 是所查询的点需要满足的标签集合、P 是所查询的点在属性上所需满足的条件。而关系模式是一个五元组 (d,a,T,P,I),其中 d 为关系模式的方向、a 是所查询关系对应的变量名、T 是所查询关系需要满足的边类型集合、P 是所查询关系在边属性上所需满足的条件、I 表示所查询关系的可以对应多少条边。
    • 子句:就是表示一个从表到表到函数。Cypher 中最主要子句的就是 MATCH 子句,表示一个图模式在属性图上的匹配,并将一张空表加入匹配结果进而形成一张新表作为结果。MATCH 子句后面跟的就是图模式,可以是点模式,也可以是点模式加关系模式加另外的模式。除了 MATCH 子句外,Cypher 还提供了 UNWIND 和 WITH 子句。UNWIND 子句用来将链表进行展开,使链表中每一个元素对应表中的一行;WITH 子句用来对表进行映射操作。
    • 查询:若干子句组合并以 RETURN 语句结尾或者多个查询的并

比较

术语GremlinCyphernGQL
VertexNodeVertex
EdgeRelationshipEdge
点类型LabelLabelTag
边类型labelRelationship Typeedge type
点 IDvidid(n)vid
边 IDeidid(r)
插入addcreateinsert
删除dropdeletedelete/drop
更新属性setPropertysetupdate

数据类型

1
2
3
4
5
6
7
8
9
boolean: 布尔文字:true, false
byte: 8位整数
short: 16位整数
int: 32位整数
long: 64位整数
float: 32位浮点数
double: 64位浮点数
char: 16位字符
String: 字符串
1
2
3
4
5
6
7
8
create:创建,创建节点、关系和属性
match:匹配,检索有关节点、关系和属性数据
return: 返回,返回查询结果
where: 哪里,提供条件过滤检索数据
delete: 删除,删除节点和关系
remove: 移除,删除节点和关系的属性
order by:以...排序,排序检索数据
set: 组,添加或更新标签
  • 常用 cql 函数
1
2
3
String:使用string字面量
aggregation: 聚合,对CQL查询结果进行一些聚合操作
relationship: 获取关系的细节,如strtnode,endnode等

基本概念

1
2
3
4
5
6
7
8
9
10
11
12
13
// --- 节点 ---
() //匿名节点
(n) //任意节点n
(:Person) //匿名且为Person类型的节点
(p:Person) //(变量:类型) 任意为Person类型的节点p
(n:Person {name:"Sidney Zhang", age:35, gender:"man"})
// --- 关系 ---
(n:Person {name:"Sidney Zhang"})-[r:LOVE]->(m:Person {name:"Jossitix Z"})
(n:Person {name:"Sidney Zhang"})-[:LOVE]->(m:Person)
(n:Person)-[rel]->(m:Person)
(n:Person)-[rel]-(m:Person)
// 关系也可带属性
()-[:WATCH {times:4, duration:1203, duration_unit:"s"}]->()
1
2
3
4
5
6
// 创建节点
CREATE (p:Person {name:"Sidney Zhang"})
CREATE (p:Person:人 {name:"Jossitix Z"}) //多个标签

// 删除数据库中所有
MATCH (n) DETACH DELETE n

导入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
neo4j-admin import --database=graph.db --ignore-extra-columns --ignore-empty-strings --id-type=STRING --skip-duplicate-nodes --bad-tolerance=10000 --nodes=E:\neo4j\import\xxx.csv --relationships=E:\neo4j\import\xxx.csv


bin/neo4j-admin import
--mode csv
--database=graph.db
--nodes:Move ./import/movies.csv
--nodes:Actor ./import/actors.csv
--relationships:Role ./import/roles.csv


neo4j-import --into /home/wkq/neo4j/neo4j-community-3.1.0/data/databases/test_10000000_graph.db --nodes /home/wkq/neo4j/node.csv --relationships /home/wkq/neo4j/relathionship.csv --trim-strings true --input-encoding UTF-8 --id-type String --stacktrace true --bad-tolerance 0 --skip-bad-relationships true --skip-duplicate-nodes false


neo4j-admin import --database=movies --nodes=import/neo4j_example/movies.csv --nodes=import/neo4j_example/actors.csv --relationships=import/neo4j_example/roles.csv

# 执行数据导入命令neo4j-admin
neo4j-admin import \
--database=graph.db
--nodes:phone="../phone_header.csv,phones.csv \
--ignore-duplicate-nodes=true \
--ignore-missing-nodes=true \
--relationships:call="../call_header.csv,call.csv"
# 重启neo4j服务
neo4j start

csv 文件

  • nodes
    • movieId:ID:唯一标识,用于后面创建关系
    • :LABEL:标签,多个用 : 分隔
1
2
3
4
movieId:ID,title,year:int,:LABEL
tt0133093,"The Matrix",1999,Movie
tt0234215,"The Matrix Reloaded",2003,Movie;Sequel
tt0242653,"The Matrix Revolutions",2003,Movie;Sequel
1
2
3
4
personId:ID,name,:LABEL
keanu,"Keanu Reeves",Actor
laurence,"Laurence Fishburne",Actor
carrieanne,"Carrie-Anne Moss",Actor
1
2
3
4
5
uuid:ID(Person),name:String,:Label
2a3e275d9abc4c45913d8e7e619db87a,"张忆耕",Label1
db6ee76baff64db5956b6a5deb80acbf,"傅某评",Label2
8d2f4a74e7e7429390b3389d64d77637,"王苏维",Label3
4d0a5c3fa89a49e89f81a152f2aa259c,"蓝波",Label4
  • relations
    • role:关系的属性
    • :TYPE:可以理解为关系的标签
1
2
3
4
5
6
7
8
9
10
:START_ID,role,:END_ID,:TYPE
keanu,"Neo",tt0133093,ACTED_IN
keanu,"Neo",tt0234215,ACTED_IN
keanu,"Neo",tt0242653,ACTED_IN
laurence,"Morpheus",tt0133093,ACTED_IN
laurence,"Morpheus",tt0234215,ACTED_IN
laurence,"Morpheus",tt0242653,ACTED_IN
carrieanne,"Trinity",tt0133093,ACTED_IN
carrieanne,"Trinity",tt0234215,ACTED_IN
carrieanne,"Trinity",tt0242653,ACTED_IN
1
2
3
:START_ID(Person),:END_ID(Person),:TYPE
2a3e275d9abc4c45913d8e7e619db87a,db6ee76baff64db5956b6a5deb80acbf,Relationship1
8d2f4a74e7e7429390b3389d64d77637,4d0a5c3fa89a49e89f81a152f2aa259c,Relationship2

配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
 修改第22行load csv时l路径,在前面加个#,可从任意路径读取文件
#dbms.directories.import=import

# 修改35行和36行,设置JVM初始堆内存和JVM最大堆内存
# 生产环境给的JVM最大堆内存越大越好,但是要小于机器的物理内存
dbms.memory.heap.initial_size=5g
dbms.memory.heap.max_size=10g

# 修改46行,可以认为这个是缓存,如果机器配置高,这个越大越好
dbms.memory.pagecache.size=10g

# 修改54行,去掉改行的#,可以远程通过ip访问neo4j数据库
dbms.connectors.default_listen_address=0.0.0.0 #允许远程访问
dbms.connector.bolt.enabled=true #开启bolt服务,默认端口7687
dbms.connector.http.enabled=true #开启http服务,默认端口7474
dbms.connector.https.enabled=true #开启https服务,默认端口7473

# 默认 bolt端口是7687,http端口是7474,https关口是7473,不修改下面3项也可以
# 修改71行,去掉#,设置http端口为7687,端口可以自定义,只要不和其他端口冲突就行
#dbms.connector.bolt.listen_address=:7687

# 修改75行,去掉#,设置http端口为7474,端口可以自定义,只要不和其他端口冲突就行
dbms.connector.http.listen_address=:7474

# 修改79行,去掉#,设置http端口为7473,端口可以自定义,只要不和其他端口冲突就行
dbms.connector.https.listen_address=:7473

# 修改227行,去掉#,允许从远程url来load csv
dbms.security.allow_csv_import_from_file_urls=true

# 修改246行,允许使用neo4j-shell,类似于mysql 命令行之类的
dbms.shell.enabled=true

# 修改235行,去掉#,设置连接neo4j-shell的端口,一般都是localhost或者127.0.0.1,这样安全,其他地址的话,一般使用https就行
dbms.shell.host=127.0.0.1

# 修改250行,去掉#,设置neo4j-shell端口,端口可以自定义,只要不和其他端口冲突就行
dbms.shell.port=1337

# 修改254行,设置neo4j可读可写
dbms.read_only=false
1
2
# 浏览器改密码
:server change-pas

py2neo

#最佳实践

py2neo:通过操作 python 变量,实现操作 neo4j

neo4j:python 基于 bolt 协议连接,比 py2neo 复杂,核心是执行 CQL 语句,OUT

基本使用

1
pip install py2neo
1
2
3
from py2neo import Graph, Node, Relationship, Subgraph, NodeMatcher, RelationshipMatcher
# 连接
graph = Graph('http://localhost:7474', name="neo4j", password="")

图 -graph

1
2
3
4
graph.schema.node_labels          # 查看节点的所有标签,返回frozenset,通过循环调用返回str
graph.schema.relationship_types # 查看关系的所有类型,返回frozenset,通过循环调用返回str

graph.merge(node) # 将重复数据去除掉,只保留第一个

遇到的问题

输出的中文的 unicode 变为正常

1
2
3
4
5
6
7
# 第230行的 utf-8 改为 unicode-escape
for i in range(0, len(parts), 2):
parts[i] = (X_ESCAPE.sub(u"\\\\u00\\2", parts[i].encode("unicode-escape").decode("utf-8")).

# 即改后为
for i in range(0, len(parts), 2):
parts[i] = (X_ESCAPE.sub(u"\\\\u00\\2", parts[i].encode("unicode-escape").decode("unicode-escape")).

lancedb

lancedb:矢量数据库

networkx

networkx:用于创建、操作和研究图(网络)结构

  • 简单易用、可扩展性强
  • 性能不足,OUT
量级(节点数)边数参考(稀疏图)性能表现(纯 Python 环境)典型适用场景
1k 以内(千级)1k~10k秒级完成所有操作(遍历、最短路径、社区检测等),无压力教学演示、小规模数据分析、算法原型验证
1k~10k(万级)10k~100k常用操作(筛选、统计、简单图算法)流畅;复杂算法(如 Louvain 社区检测)耗时秒级~分钟级科研数据分析、中等规模网络挖掘(如社交小圈子、论文引用网络)
10k~100k(十万级)100k~1000k简单操作(节点筛选、边遍历)尚可;复杂算法(如最短路径、聚类)耗时显著(分钟级~小时级);内存占用较高(可能达 GB 级)需谨慎使用,仅适合无替代方案时的轻量操作(如仅统计节点属性)
100k 以上(百万级 +)1000k+操作卡顿严重(小时级甚至无法完成),内存易溢出;复杂算法基本不可用完全不推荐,需换用高性能图计算库

NetworkX 本质是「纯 Python 实现的图工具库」,而非「高性能图计算引擎」,限制源于三点:

  1. 底层实现:节点、边的存储依赖 Python 字典(字典的哈希表结构冗余度高,内存占用大),遍历和计算均为单线程 Python 解释执行,比 C/C++ 实现慢 10~100 倍。
  2. 内存管理:Python 对大对象的内存回收效率较低,十万级以上节点的图会占用大量内存(例如:10 万节点的稀疏图约占数百 MB 内存,100 万节点可能达数 GB)。
  3. 算法优化:部分复杂算法(如社区检测、图聚类)未做并行化或底层优化,仅适合小规模验证。
给博主来一杯卡布奇诺