简介:Neo4j 是一款面向复杂关系数据的图形数据库管理系统,社区版免费且适用于非商业项目,可满足开发者、数据工程师在社交网络、知识图谱、推荐系统等场景下的建模与分析需求。这份打包资源提供 Neo4j 社区版 5.17.0,共 211 个文件,约 108 MB,包含核心运行库、APOC 插件、Cypher Shell 命令行工具、服务启动脚本与配置文件,其中 jar 依赖库、exe 服务程序、bat 启动脚本一应俱全,解压后即可配置环境并启动服务。已有 557 人学习,适合正在学习 Cypher 查询语言、图数据建模或需要搭建本地图数据库实验环境的中级用户。借助该资源可快速完成免安装部署,并使用图查询与 APOC 扩展功能,为后续开发图应用或研究图算法提供可直接运行的基础。
1. Neo4j 社区版:装之前先把边界和能力对齐
先说一个反直觉的结论:Neo4j 社区版不是企业版的“少几个按钮”版本,它缺的恰好是生产环境最容易踩到的那几件事——在线备份、角色权限、多数据库和集群。很多人下载社区版时装得很快,装完才发现局域网 IP 访问不通、想开两个库不行、APOC 插件版本对不上,于是花半天时间去跟一个本身就不存在的功能较劲。社区版适合单机、学习图数据库、原型验证和中小型内部工具,它最擅长的是把复杂的多表 JOIN 变成直观的图遍历,Cypher 在单机上的性能也够用。这篇笔记不做科普,直接按“选型、安装、查询、导入、排错”往下走,新手能跟着落地,老手也能直接拿去当对照清单。
2. 下载与安装:版本选型、离线包和 Docker 数据卷
2.1 先看明白三个发行版再动手
Neo4j 官方提供三样东西:社区版 Server、企业版、以及 Desktop 图形客户端。很多人会把 Desktop 当成“Neo4j 本体”,其实 Desktop 只是一个管理器,真正干活的引擎仍然是社区版或企业版。我的建议是:在服务器或生产环境里,直接下载 Community Server 的压缩包,不要装 Desktop,也不要在 Linux 上硬开图形界面。
社区版和企业版的功能边界非常重要,提前看清能省掉后面一整天的折腾。差别主要集中在运维和权限层面,而不是查询能力本身。
| 能力 | 社区版 | 企业版 |
|---|---|---|
| 在线备份 | 无 | 有 |
| 角色权限(RBAC) | 无 | 有 |
| 多数据库 | 单用户库 | 支持 |
| 集群 / 高可用 | 无 | 有 |
| APOC 核心过程 | 可安装 | 可安装 |
| 图算法库 GDS | 不适用 | 单独授权 |
补充一点,Neo4j 4.x 和 5.x 的社区版默认都只能存在一个用户数据库(系统库不算在内)。如果你想做多租户隔离,社区版里更务实的做法不是开多个数据库,而是用 Docker 跑多个容器,每个容器一个实例。
下载地址就是 Neo4j 官网 Download Center,选择 Community Server 版本。社区版是免费下载且可商用的,许可证是 GPL v3。这里有个值得注意的细节:官网默认给你推荐最新版,但如果你是做长期项目,我建议优先看有没有 4.4 这种 LTS 版本可用。版本选择直接影响后续 APOC 插件和驱动兼容性,别一味追新。
2.2 Windows:ZIP 解压、服务安装和初始密码
Windows 上最常见的安装方式是把 ZIP 包解压到目录,然后用命令安装成 Windows 服务。下载下来的包解压后会看到 bin、conf、data、import、plugins 这几个目录,bin下的neo4j.bat就是日常操作入口。
D:\neo4j\bin\neo4j.bat install-service D:\neo4j\bin\neo4j.bat start D:\neo4j\bin\neo4j.bat statusinstall-service是把 Neo4j 注册成 Windows 服务,需要以管理员身份打开 CMD;start是启动服务,status用来确认启动状态。启动成功后,浏览器打开http://localhost:7474,首次登录会要求你改密码。
这里有个很多人必踩的坑:忘了密码之后没有“找回密码”按钮,能救你的是重置命令。Neo4j 5.x 下用neo4j-admin dbms set-initial-password,4.x 的写法略有不同,具体以你这个版本在bin/neo4j-admin help里输出的子命令为准。执行重置前要先把 Neo4j 停下,否则会报文件占用。
2.3 Linux 离线安装:没有内网环境也要先准备 JAVA_HOME
离线安装是 Linux 服务器上最常见的诉求。你在有网机器上把 tar.gz 包下载好,传到内网机器上解压就行。下载链接的格式一般是:
wget https://dist.neo4j.org/neo4j-community-<版本号>-unix.tar.gz tar -xzf neo4j-community-<版本号>-unix.tar.gz mv neo4j-community-<版本号> /usr/local/neo4j解压后不要急着启动,先检查 Java。Neo4j 5.x 要求 JDK 17,4.4 LTS 要求 JDK 11。服务器上经常同时存在多个 JDK,最常见的问题是java -version显示 1.8,启动脚本直接报Unsupported Java version。不要为了 Neo4j 去改系统全局 JDK,那样会影响其他服务,正确做法是在启动前单独导出变量:
export JAVA_HOME=/opt/jdk-17 export PATH=$JAVA_HOME/bin:$PATH bin/neo4j start前台启动用bin/neo4j console,日志会直接打到终端,调试配置问题比start更直观。另外,root 环境下可以用bin/neo4j install-service安装 systemd 服务,之后就能用systemctl start neo4j来管理。
提示:离线环境如果没有 JDK 17,也要提前把 JDK 的 tar 包一起带进去。Neo4j 不会自带 JDK,别看它是个 Java 应用就以为免 JDK。
2.4 Docker 部署:环境变量改配置,数据卷必须挂
如果不想碰 Java 环境,Docker 是启动 Neo4j 最省事的方式。官方镜像neo4j:5和neo4j:4.4都在 Docker Hub 上长期维护。一个可用的启动命令是这样:
docker run -d --name neo4j \ -p 7474:7474 -p 7687:7687 \ -e NEO4J_AUTH=neo4j/你的密码 \ -e NEO4J_dbms_memory_heap_max__size=512M \ -e NEO4J_server_default__listen__address=0.0.0.0 \ -v neo4j_data:/data \ -v neo4j_logs:/logs \ neo4j:57474 是 HTTP 端口,给浏览器界面和 REST API 用;7687 是 Bolt 端口,给 Java、Python 等驱动用。很多人在浏览器里打开了 7474,但程序连接 7687 失败,就是因为安全组只放行了一个端口。
容器里的配置修改不是去改neo4j.conf,而是通过NEO4J_开头的环境变量覆盖。命名规则是单下划线代表配置里的点,双下划线代表配置里的下划线。NEO4J_dbms_memory_heap_max__size会转换成dbms.memory.heap.max_size,NEO4J_server_default__listen__address会转换成server.default_listen_address。不理解这个规则的人,会把变量名写成NEO4J_DBMS_MEMORY_HEAP_MAX_SIZE,结果配置完全没生效。
数据卷必须挂载,否则容器删掉后数据库就没了。neo4j_data:/data里的neo4j_data是命名卷,宿主机路径由 Docker 管理;如果你希望放到指定目录,就改成-v $PWD/neo4j-data:/data。
2.5 启动后先做一轮冒烟验证
装好之后不要先急着导数据,先用一行 Cypher 确认服务和驱动通路是正常的:
bin/cypher-shell -u neo4j -p 你的密码 "RETURN 1 AS ok;"能返回ok = 1,说明 Bolt 协议、认证和 JVM 都通了。再用ss -lntp | grep 7474和ss -lntp | grep 7687确认端口监听正常。这一步花不到一分钟,但能把环境问题和业务代码问题隔离开,避免后面排查时两头怀疑。
3. Cypher 查询实战:从一个节点出发捞多条关系,并抓慢查询源头
3.1 先把关系翻译成图模式
刚接触 Cypher 的人喜欢拿它跟 SQL 对比,但图查询的核心不是 SELECT,而是模式匹配。模式(p:Person {name:'张三'})-[r:KNOWS]->(f:Person)就是在图上找一个三角形结构:起始节点带标签 Person 且 name 属性为张三,出一条 KNOWS 关系,到达另一个 Person 节点。变量p、r、f会被绑定到匹配到的元素上。
MATCH (p:Person {name:'张三'})-[r:KNOWS]->(f:Person) RETURN p, r, f这种写法好在哪里?它把“张三认识谁”直接翻译成了图的遍历,而不是多表 JOIN。如果给 Person.name 建过索引或唯一约束,Neo4j 会直接定位到张三这个节点,然后只遍历他个人的出边,查询代价和整体图的大小无关。这也是图数据库在社交、知识图谱场景下比关系型数据库更适合的原因。
3.2 从一个节点出发,怎么查“多条”
搜索这个问题的人,通常有三种“多条”的意思,写法完全不一样。
第一种是“多种关系类型都查”。比如既要查张三认识的人,又要查和他一起工作的人。关系类型用竖线组合:
MATCH (p:Person {name:'张三'})-[r:KNOWS|:WORKS_WITH]->(f) RETURN DISTINCT f.name, type(r) AS relationKNOWS|:WORKS_WITH是关系类型的或条件,type(r)可以返回实际命中的关系类型。加DISTINCT是因为同一个人可能既是朋友又是同事,这样会避免重复行。
第二种是“沿一条关系类型走多跳”。比如查张三两度人脉里的人,这就要用可变长度模式:
MATCH path = (p:Person {name:'张三'})-[:KNOWS*1..3]-(f) RETURN path*1..3表示最少 1 跳,最多 3 跳。注意这里我把关系方向写成了无向--,社交关系通常是双向的。这种查询看起来短,实际执行可能非常贵,一个社交网络里每个人的朋友数量如果是几百,3 跳之后的行数就是几百万量级。所以可控深度的同时,最好在查询里配合LIMIT。
第三种是“从同一个人出发,拿多个维度的集合”。比如既要朋友列表,又要工作单位列表。如果直接写在一个 MATCH 里会出现笛卡尔积,每个朋友都要和每个工作单位组合,结果里产生大量无用行。我一般拆开写:
MATCH (p:Person {name:'张三'}) OPTIONAL MATCH (p)-[:KNOWS]->(friend) OPTIONAL MATCH (p)-[:WORKS_AT]->(work) RETURN collect(DISTINCT friend.name) AS friends, collect(DISTINCT work.name) AS workplacesOPTIONAL MATCH保证某一边没有结果时,其他维度的结果不会被整体丢掉。collect负责把多行聚合成一个列表,加上DISTINCT去重。这也是“从一个节点出发查多条”最常见的正确姿势。
3.3 站在一个节点看一圈:聚合和路径去重
更典型的场景是知识图谱里的“找同事”。从张三出发到项目节点,再从项目节点反向找到所有参与同一项目的人,排除张三自己:
MATCH (p:Person {name:'张三'})-[:PARTICIPATED_IN]->(proj:Project)<-[:PARTICIPATED_IN]-(c:Person) WHERE c <> p RETURN proj.name, collect(DISTINCT c.name) AS colleagues这个查询的本质是路径合并,张三的每个项目都把所有参与者带回来,按项目聚合。WHERE c <> p是比较节点的引用,不是 name 字符串,可以避免同名问题。这里用collect(DISTINCT c.name)而不是collect(c.name),因为同一个人可能在一个项目里有多个角色,会导致重复。
社区版没有 GDS 图算法库,所以类似度中心性这种指标可以自己手写。比如统计张三每种关系的出度:
MATCH (p:Person {name:'张三'})-[r]->() RETURN type(r) AS relation, count(*) AS cnt不限定关系类型,[r]会匹配所有出边,然后按关系类型分组计数。这个结果可以直接进入前端展示,也可以作为下一步筛选的输入。
3.4 慢查询的源头:全表扫描和索引缺失
知道了怎么写,还得知道怎么写快。Cypher 是声明式语言,你不能强迫它走某条执行计划,只能用EXPLAIN和PROFILE看它实际怎么跑:
PROFILE MATCH (p:Person {name:'张三'})-[:KNOWS]->(f) RETURN f.name在结果里看算子名称。如果看到NodeByLabelScan,说明它是把所有人先扫一遍再过滤,这就是慢查询的根源;如果看到NodeIndexSeek或NodeUniqueIndexSeek,说明它直接通过索引定位到了张三。绝大多数“为什么这么慢”的问题,到这里就真相大白了。
通常我们需要让查询谓词落到索引上。建立方式有两种,首先是唯一约束:
CREATE CONSTRAINT person_name_unique FOR (p:Person) REQUIRE p.name IS UNIQUE;这个约束本身就是索引,还能防止导入数据时重复。如果你只是需要普通索引,不要求唯一性:
CREATE INDEX person_name_idx FOR (p:Person) ON (p.name);Neo4j 5.x 还提供全文索引,适合CONTAINS这类模糊查询,但在 4.4 里语法是过程调用,两者不一样。我的习惯是:能精确等值匹配就建唯一约束,必须CONTAINS才上全文索引;不要给每个属性都建索引,写入性能会被拖垮。
4. 导入数据:LOAD CSV 与 neo4j-admin import 两条路,把知识图谱建起来
4.1 先根据数据量选导入工具
“社区版怎么导入数据”这个问题,答案取决于数据的量级和场景。社区版没有企业版的 GUI 批量导入工具,但命令行和 Cypher 这两条路足够日常使用。
| 方式 | 适合规模 | 在线 / 离线 | 适用场景 |
|---|---|---|---|
| LOAD CSV | 十万级以内 | 在线 | 增量导入、单条业务写入、边导边查 |
| neo4j-admin import | 百万级以上 | 离线 | 首次全量导入、重建整个库 |
如果用 LOAD CSV 导几十万行甚至上百万行,不是不行,但事务日志会非常难处理,中途出错回滚也麻烦。数据量大到一定程度,就应该切成 CSV 文件用neo4j-admin import离线导,速度上有数量级的差别。
4.2 LOAD CSV:唯一约束、类型转换和空字符串
LOAD CSV 读取的是import目录下的文件,Cypher 里的file:///persons.csv三个斜杠后面是相对路径。先建约束,再去执行导入,这个顺序很重要:
CREATE CONSTRAINT person_id_unique FOR (p:Person) REQUIRE p.id IS UNIQUE; LOAD CSV WITH HEADERS FROM 'file:///persons.csv' AS row MERGE (p:Person {id: row.id}) SET p.name = trim(row.name), p.age = toInteger(row.age);MERGE是按 id 找节点,找到就更新,找不到就创建;SET负责更新属性。这里有个新手最容易忽略的点:LOAD CSV 读出来的所有字段都是字符串,age即使 CSV 里是数字,也必须用toInteger转换。而toInteger('')会直接报错,因为空字符串不是 null。安全写法是:
SET p.age = CASE WHEN trim(row.age) = '' THEN null ELSE toInteger(row.age) ENDCVS 文件里的缺失字段会被当成 null,但空字符串不会,这是两个不同的情况。所以在做类型转换前,统一对空字符串做判断,避免导入跑到一半失败。
关系数据同样可以用 LOAD CSV 导入。比如友情关系文件:
LOAD CSV WITH HEADERS FROM 'file:///friendships.csv' AS row MATCH (a:Person {id: row.person_id}) MATCH (b:Person {id: row.friend_id}) MERGE (a)-[k:KNOWS]->(b) SET k.since = toInteger(row.since);两个 MATCH 分开写,比写成一个 MATCH 更好理解,而且在任意一边节点不存在时,这一行数据会被跳过而不是报错。MERGE到关系上,是防止同一对节点之间重复创建关系。
4.3 neo4j-admin import:百万节点离线全量导入
对于首次全量导入,我一般用neo4j-admin import。它不需要启动数据库,直接读 CSV 头文件和数据文件,写入新的数据存储。先准备头文件:
personId:ID(PersonId),name,age:INT,:LABEL数据文件:
p1,张三,30,Person p2,李四,26,Person关系头文件:
:START_ID(PersonId),:END_ID(PersonId),since:INT关系数据文件:
p1,p2,2020然后执行导入命令:
bin/neo4j-admin import --database=neo4j \ --nodes=import/persons_header.csv,import/persons.csv \ --relationships=KNOWS=import/friends_header.csv,import/friends.csv \ --skip-bad-relationships=true \ --overwrite-destination=true:ID(PersonId)表示 id 属于 PersonId 这个命名空间,关系头文件里的:START_ID(PersonId)和:END_ID(PersonId)同样引用这个命名空间,导入器才能把人和人关联起来。:LABEL列写节点标签,多个标签用分号分隔。:INT声明属性类型,导入器会直接转成整数,不用再靠 Cypher 转。
参数里值得留意的是--skip-bad-relationships。写true时,找不到端点的关系会被跳过,适合清洗不干净的数据;如果希望一条坏数据都不容忍,就改成false,先把 CSV 洗干净再导。--overwrite-destination=true用于覆盖目标库,如果你是为了修数据重导,这个参数能省去删库步骤。
注意:执行导入前必须停止 Neo4j,导入命令会直接操作数据文件。新版本里命令也可能是neo4j-admin database import,老版本是neo4j-admin import,参数一致,用你版本帮助文档里的为准。导入完成后启动服务,再跑一条 count 验证:
MATCH (n:Person) RETURN count(n);4.4 一套可直接套用的知识图谱建库流程
这里给一个可复用的小流程,我处理“人物关系知识图谱”时一直这么干:
- 实体抽到一个或多个节点 CSV,每一行必须有稳定的唯一 ID,比如身份证号、手机号或者业务主键。
- 关系抽到多个关系 CSV,每条关系必须能双向找到端点 ID。
- 先建唯一约束,再导节点,最后导关系。
- 关系带时间、权重等属性时,放在关系 CSV 里,不要塞进节点。
这套规范化流程对知识图谱后期维护特别关键。很多人一开始偷懒,把关系属性全堆在节点 JSON 里,结果图谱变成了一张大宽表,图数据库的优势一点没体现。建议把节点看作“事实”,关系看作“事实之间的联系”,属性只放该事实本身的信息,不要放与之无关的上下文。
5. 避坑:社区版安装和使用中的五个高频翻车点
5.1 本机能连,局域网机器连不上
现象:浏览器访问http://localhost:7474一切正常,但同事通过http://192.168.x.x:7474访问,页面一直转圈或拒绝连接。
原因:Neo4j 默认监听在 127.0.0.1,只接受本机连接。这个配置不是 bug,是安全考虑,但放在内网服务器上就会让人困惑。
解决:修改conf/neo4j.conf里的监听地址。Neo4j 5.x 用:
server.default_listen_address=0.0.0.0Neo4j 4.4 用:
dbms.connectors.default_listen_address=0.0.0.0改完重启。另外检查云服务器安全组是否同时放行了 7474 和 7687,Browser 页面本身跑在 7474,但浏览器里的查询是通过 Bolt 协议走 7687 发到后端的。只放行 7474,页面能打开但所有查询都会提示无法连接。
5.2 改了内存参数,感觉像没改过
现象:修改dbms.memory.heap.max_size和dbms.memory.pagecache.size后重启,用SHOW SETTING一看,数值还是老样子,数据库内存占用也没有变化。
原因:最常见的是改错了配置文件或者改完没重启。Neo4j 可以读取conf/neo4j.conf,但如果你的工作目录不在 Neo4j 目录下,脚本可能找到的是另一个 HOME。Docker 场景下,进容器改neo4j.conf也不是好办法,容器重建后会被镜像层覆盖。
解决:先用bin/neo4j console前台启动,观察它打印出来的配置路径;再执行:
SHOW SETTING dbms.memory.heap.max_size;确认当前生效值。Docker 里不要改文件,用环境变量注入:
-e NEO4J_dbms_memory_heap_max__size=1G这里的命名规则是单下划线替代点、双下划线替代下划线,别想当然写成全大写加单下划线。
5.3 APOC 装上后直接启动失败
现象:从 GitHub 下载了最新 APOC jar,放进plugins目录,重启后 Neo4j 报版本不匹配或者找不到依赖,apoc函数也调用不了。
原因:APOC 和 Neo4j 版本必须严格对应,GitHub 上的最新源码不一定匹配你安装的 Neo4j 版本。另外一个原因是 APOC 还分 Core 和 Extended,部分 Extended 过程需要企业版授权,社区版装上去会没有许可证。GDS 更是如此,它本身是企业版图算法库,社区版不是“配置一下就能用”的。
解决:去 Neo4j 官网的下载中心,找和你 Neo4j 版本同号段的 APOC 包。安装后在neo4j.conf里加:
dbms.security.procedures.unrestricted=apoc.*不加这段,APOC 过程会被安全机制拦截。如果你只是用到基础 APOC 功能,社区版完全够;想跑 PageRank 这类图算法,就得评估企业版,别在社区版上浪费时间折腾 GDS。
5.4 中文乱码和空字符串把导入搞崩
现象:Excel 导出的 CSV,用 LOAD CSV 导入后中文全是“锟斤拷”;或者toInteger(row.age)报错,提示无法把''转成整数;还有部分行导进去了,但年龄字段变成了 null。
原因:中文 Windows 下 Excel 默认导出的 CSV 是 ANSI 编码,Neo4j 只认 UTF-8。另外 LOAD CSV 把所有字段都当字符串,空单元格是空字符串而不是 null,直接toInteger会抛异常。
解决:在 Excel 中另存为“CSV UTF-8”格式,或者用文本编辑器把文件转成 UTF-8 并去掉 BOM。类型转换前先处理空字符串:
CASE WHEN trim(row.age) = '' THEN null ELSE toInteger(row.age) ENDneo4j-admin import 场景下,在头文件里声明age:INT,导入器会自动处理类型,空字符串同样会变成 null,比 LOAD CSV 在这种场景下更省心。
5.5 容器数据跟着容器一起消失
现象:用 Docker 跑 Neo4j,测试完docker rm删掉容器重新创建,发现之前建的所有节点和关系都没了,像没导过数据一样。
原因:Neo4j 容器里的数据目录是/data,如果不挂载数据卷,数据就写在容器层里。容器删除后,这一层也跟着销毁。很多人把-v neo4j_data:/data写漏了,或者挂载到了宿主机一个权限不对的目录,容器启动后 Neo4j 没有写权限直接退出。
解决:启动命令里务必加:
-v neo4j_data:/data如果要挂到宿主机目录:
-v $PWD/neo4j-data:/data宿主机目录如果权限不对,容器会启动失败。通用处理是把目录权限交给容器内进程用户,比如chown -R 1000:1000 ./neo4j-data,具体 UID 以你拉取的镜像为准。容器重建后,数据卷还在,图谱就不会丢。
6. 收尾:装完只跑通还不够,验证、备份和索引这步不能省
6.1 十分钟完成一次完整验证
新环境装好 Neo4j 后,我习惯按这个顺序走一遍,确认不是“表面能用”:
bin/neo4j version bin/neo4j status bin/cypher-shell -u neo4j -p 你的密码 "SHOW DATABASES;"SHOW DATABASES至少要能看到neo4j这个库,状态是 online。然后跑一条 EXPLAIN 看看索引是否生效:
EXPLAIN MATCH (p:Person {name:'张三'}) RETURN p;如果算子显示 NodeIndexSeek 而不是 NodeByLabelScan,说明索引这条路通了。这套验证做完,环境才算真正可用。
6.2 没有热备份,就用停机冷备
社区版没有企业版那样的在线备份命令,这是硬边界。我一般用停机冷备,简单且可靠:
bin/neo4j stop tar -czf neo4j-backup-$(date +%F).tgz data/databases/neo4j bin/neo4j startDocker 部署时先停容器,再用临时容器打包数据卷:
docker stop neo4j docker run --rm -v neo4j_data:/data -v $PWD:/backup alpine tar czf /backup/neo4j-data.tgz -C /data . docker start neo4j备份恢复时把数据卷内容解回去即可。这个方法虽然笨,但不会破坏文件一致性,比直接用docker cp从运行中的容器里拷数据要安全得多。
6.3 普通代码能跑,索引不能少
从一次数据丢失之后我养成了一个习惯:每次用社区版建库,都会强制走一遍检查流程——先建唯一约束,再导节点,再查一次SHOW DATABASES确认实例状态,改完配置重启前先备份一份 conf 文件,最后跑一次 EXPLAIN 确认关键查询没有全表扫描。少一步,后面补的成本都会翻倍。数据量一上去,没有索引的查询慢到让人怀疑机器坏了,而建索引也就是一行命令的事。希望帮到你。
本文还有配套的精品资源,点击获取