简介:面向图数据库学习者与Java后端开发者的Neo4j社区版5.24.2类Unix安装包,适合搭建知识图谱、社交网络、推荐系统与欺诈检测等图形关系实验环境。压缩包共257个文件、约122.36MB,以238个jar依赖包为主体,辅以7个txt说明、2个xml与2个conf配置文件,并包含cypher-shell、neo4j-admin等命令行脚本及服务启动入口。已有360人学习下载,说明其在图数据库入门与项目预研中有一定参考价值。资源保留社区版核心图数据库能力,支持ACID事务与原生Cypher查询,便于读者快速验证数据建模、关系查询和图形界面操作;配置文件与命令行工具齐全,也适合用于教学演示和本地开发调试,尤其适合需要理解节点与关系建模、练习Cypher语法以及开展小型图数据项目的读者。
1. 图数据库 Neo4j 5.24.2 的 unix.tar 包:一台不联网的内网机器,怎么把它跑起来
有些活是在没有外网的机器上干的:客户机房里放一台内网服务器,装不了包管理器里那些要联网拉依赖的东西,运维也不允许随便加系统源。这时候neo4j-community-5.24.2-unix.tar这种整包就派上用场了——一个压缩包解到自己的目录里,配好 JDK,启动脚本一跑,Bolt 和 HTTP 两个端口就开了,整个过程不动系统目录、不碰全局配置。它是 Neo4j 社区版的完整发行包,5.24 属于 5.x 的长期支持分支,属性图模型、Cypher 查询、约束索引、CSV 批量导入、社区版单机主从这些都齐了。适合谁?做知识图谱原型、关系链路分析、权限/推荐这类多跳查询的团队,手上一台 8 核 32G 的机器就能开干。不适合谁?需要集群写扩展、需要企业级热备份和细粒度权限的场景,那些得换版本。
2. 解压之前先核 Java:tar 包的目录布局与启动链路
tar 包跟 apt/yum 装出来的 Neo4j 最大的区别是「一切都归你管」。系统包装完会自动建neo4j用户、写 systemd 单元、把/var/lib/neo4j分好区;tar 包什么都不做,它只是一个解压即用的目录树,跑在哪个用户下、数据放哪块盘、开机要不要自启,全得自己填。这件事第一次接触会觉得麻烦,做过两三次之后反而是我更喜欢它的原因:环境边界干净,出问题不会牵扯到系统层面。
2.1 选 tar 包而不是包管理器的四个现实理由
第一个理由是离线。很多内网环境装不了新包,你没法执行apt install neo4j,但你可以把一个 100 多 MB 的压缩包拷进去。第二个理由是多版本并存,一台机器上想同时留 5.24 和 4.4 做迁移对比,解压到两个不同目录、用不同端口就行,包管理器装两个大版本经常打架。第三个理由是非 root 部署,解压到/opt/neo4j或者用户 home 下的目录,用普通账号就能跑,不必申请 sudo。第四个理由是卸载,删目录就完事,不会留下散落在/etc、/var、/usr/share里的残留文件互相牵扯。
代价也很清楚:服务自启、日志轮转、文件句柄上限、数据目录权限,这些原本由系统包替你做的事全部变成手工项。后面几章里的坑,八成以上都来自这里。
2.2 解压后每个目录负责什么
先把压缩包解开,看清结构再动手配置。Neo4j 5.24 的目录命名比较直白,但有几个目录容易被误用,尤其是import和data混着放东西的情况。
| 目录 | 职责 | 容易踩的点 |
|---|---|---|
bin/ | 启动、停止、导入、备份等所有命令行入口 | 脚本依赖JAVA_HOME或 PATH 中的 java |
conf/ | neo4j.conf、日志配置 | 只有一份主配置,改错没语法提示 |
data/ | 数据库文件、事务日志 | 必须整块盘给,别和数据文件混放 |
import/ | LOAD CSV的默认相对路径根 | 权限不对会报「找不到文件」而不是「没权限」 |
logs/ | neo4j.log、debug.log、query.log | 查询日志默认不开,排慢查询时要手动开 |
plugins/ | APOC 等扩展 jar | 版本必须跟主版本对齐 |
labs/ | 实验性组件 | 生产环境别乱放 |
import目录值得单独说一句:Cypher 里写file:///xxx.csv的时候,这个路径不是文件系统根,而是相对于server.directories.import指向的目录。新手最容易在这里翻车,明明ls能看到文件,查询却报找不到。
2.3 Java 核验与解压落地
Neo4j 5.24 这一代要求 Java 17 起步,Java 21 也在支持矩阵里。系统自带的 JDK 8 或 11 会让启动脚本直接退出,报的错还不一定直白,所以第一步永远是看版本。
# 1) 确认当前 java 版本,必须是 17 或 21 java -version # 2) 如果系统默认是 8/11,指定一个 17 的 JDK 路径 export JAVA_HOME=/opt/jdk-17 export PATH=$JAVA_HOME/bin:$PATH # 3) 解压到部署目录(注意属主,后面要用同一个账号启动) sudo mkdir -p /opt/neo4j sudo tar -zxf neo4j-community-5.24.2-unix.tar.gz -C /opt/neo4j --strip-components=1 # 4) 目录属主交给实际运行账号,避免后面数据目录写不进去 sudo chown -R neo4j:neo4j /opt/neo4j这里--strip-components=1的作用是把压缩包内层的neo4j-community-5.24.2/这一层剥掉,让内容直接落在/opt/neo4j下。不剥的话,你的配置路径会变成/opt/neo4j/neo4j-community-5.24.2/conf/neo4j.conf,写启动脚本时容易搞混。JAVA_HOME建议写进运行账号的 profile 或者包一层的启动包装脚本,不要只在一个 SSH 会话里 export,否则换个终端启动就报「找不到 Java」。
2.4 前台启动看第一屏日志
第一次启动不要直接start扔后台,用console前台跑,日志直接打在终端上,出问题第一时间能看见。
cd /opt/neo4j # 前台启动,Ctrl+C 退出;适合首次验证和排错 bin/neo4j console # 验证通过后改成后台常驻 bin/neo4j start # 查看状态与停止 bin/neo4j status bin/neo4j stop启动成功的标志是日志里出现Started,并且 Bolt 在 7687、HTTP 在 7474 处于监听状态。用ss -lntp | grep -E '7474|7687'确认一遍比看日志更实在——日志说 Started 但端口没起来的情况,通常是监听地址被server.default_listen_address=127.0.0.1锁住了。
浏览器打开http://<内网IP>:7474会进 Neo4j Browser,默认账号密码都是neo4j,首次登录强制改密码。这一步别跳过,也别图省事改成跟数据库同名的弱口令,内网也不是保险箱。
3. neo4j.conf 里真正要动的几个参数:内存切分、监听边界与日志
conf/neo4j.conf打开有上千行注释,绝大多数保持默认就行,真正需要按机器规格调的只有那么几项。常见的错误做法是把官方示例整段复制过来,不管机器是 8G 还是 128G 都写server.memory.heap.max_size=8G,然后抱怨图数据库吃内存。参数本身没错,跟机器不匹配才是问题。
3.1 堆内存和 page cache 怎么切
Neo4j 的内存分两块,职责完全不同。堆(heap)给查询执行、事务状态、Cypher 运行时用;page cache 用来缓存图数据文件,让遍历不用每次读盘。社区版把这两块配错的后果很直接:堆给小了,大查询直接 OOM;page cache 给小了,查询不报错但慢得让人怀疑人生,磁盘 IO 一直跑满。
经验切法是:page cache 尽量能装下图数据文件的实际大小,剩下的内存给堆,堆一般给到总内存的 25% 到 50%,不要超过 31G(超过 JVM 压缩指针失效,收益反而下降)。图数据文件大小看data/databases/neo4j目录的实际占用。
# conf/neo4j.conf —— 以一台 32G 内存、图数据约 10G 的机器为例 # JVM 堆:初始值和最大值设成一样,避免运行期扩容抖动 server.memory.heap.initial_size=8g server.memory.heap.max_size=8g # page cache:略大于图数据实际大小,给增长留余量 server.memory.pagecache.size=12g三个参数里最容易被忽略的是initial_size和max_size必须写成一个值。这俩不等的时候 JVM 会做堆扩容,扩容瞬间有 STW 停顿,图数据库上表现为偶发的查询毛刺,很难归因,所以别偷这个懒。
改完内存参数,用bin/neo4j console起一次,看启动日志里 JVM 的参数回显,确认生效再转后台。参数拼写错误(比如写成server.memory.pageCache.size,大写 C)不会报错,会被当成未知配置忽略,这是 Neo4j 配置系统一个不太友好的地方,改完一定要回读日志。
3.2 监听地址与端口暴露的边界
默认配置只监听127.0.0.1,本机之外连不上。要让内网其他机器访问,得显式放开监听地址。这里有个细节:server.default_listen_address是兜底值,单独设server.bolt.listen_address和server.http.listen_address可以覆盖它。
# 监听所有网卡,适合容器或需要跨机访问的场景 server.default_listen_address=0.0.0.0 # 也可以拆开单独控制:Bolt 对内网开放,HTTP 只留本机 # server.bolt.listen_address=0.0.0.0:7687 # server.http.listen_address=127.0.0.1:7474我更倾向于拆开写。Bolt(7687)是驱动程序走的生产通道,需要给应用服务器访问;HTTP(7474)只是 Browser 和人访问的管理入口,完全可以只留本机,需要的时候用 SSH 隧道转一下。把 7474 直接暴露在网段上,等于把管理界面挂出去了。
如果确实要对整个内网开 Browser,至少确认这几件事:初始密码已改、dbms.security.auth_enabled=true(默认就是 true,别手贱关掉)、前面有反向代理做访问控制、端口不是默认的 7474。
3.3 初始密码与认证
tar 包第一次启动时会把密码设成neo4j/neo4j并标记为「必须修改」。如果想在自动化部署里直接指定密码、跳过这个交互,用neo4j-admin在首次启动前设置。
# 必须在数据库首次启动之前执行,已经初始化过再执行会报错 bin/neo4j-admin dbms set-initial-password 'YourStrongPassw0rd' # 忘记密码时:先停库,再重置 bin/neo4j stop bin/neo4j-admin dbms set-initial-password 'NewPassw0rd' bin/neo4j start这个命令只在「还没初始化认证信息」的时候有效。库已经跑起来过、密码已经改过,再执行会提示认证文件已存在。忘记密码的正路是停库之后用set-initial-password重置,不要在配置文件里关掉认证想蒙混过去——关掉认证再打开,用户和角色信息是另一套逻辑,容易把自己绕进去。
3.4 日志与慢查询开关
默认只开neo4j.log和debug.log。要排查「某条查询为什么慢」,得单独把查询日志打开。
# 开启查询日志,记录执行超过阈值的 Cypher db.logs.query.enabled=INFO db.logs.query.threshold=1s db.logs.query.parameter_logging_enabled=true db.logs.query.time_logging_enabled=truethreshold=1s的意思是超过 1 秒的查询才记录。压测阶段可以调到100ms甚至0s(全记),生产环境别这么干,日志量会失控。parameter_logging_enabled会把查询参数一起记下来,排障时能还原出具体的查询条件,代价是日志里可能出现业务数据,合规上有要求的话要评估。
日志文件在logs/query.log,格式是每行一条 JSON,grep一下"elapsedTime"就能筛出最慢的那几条。
4. 数据落地:约束、LOAD CSV 与 neo4j-admin import 的分工
配置跑通只是让库活着,真正体现价值的是把业务数据变成图。这一步的常见误区是拿到数据直接CREATE往里灌,几万条还行,上百万条就开始等到怀疑人生。Cypher 的写入路径跟导入工具差一个数量级,选错了工具,时间成本是按小时计的。
4.1 先定图模型,再动手写 Cypher
以「人员-参与-项目」这类结构为例,节点标签用Person、Project,关系类型用PARTICIPATES_IN。模型定下来之后,第一个动作不是插数据,是建约束。
// 唯一约束:同时会创建对应的索引,MERGE 依赖它才能走索引查找 CREATE CONSTRAINT person_id_unique IF NOT EXISTS FOR (p:Person) REQUIRE p.id IS UNIQUE; CREATE CONSTRAINT project_id_unique IF NOT EXISTS FOR (pr:Project) REQUIRE pr.id IS UNIQUE; // 普通索引:给过滤字段建,避免全表扫节点 CREATE INDEX person_name IF NOT EXISTS FOR (p:Person) ON (p.name);REQUIRE ... IS UNIQUE建的是唯一约束,Neo4j 会自动为它维护一个索引。这一点跟很多人想的不一样:约束不只是校验,它直接决定了MERGE的性能。没建唯一约束时,MERGE (p:Person {id: row.id})会做标签扫描,数据量上去之后每条MERGE都可能是全扫,血泪经验。建完约束用SHOW CONSTRAINTS确认状态是ONLINE再灌数据。
4.2 LOAD CSV 做增量与中小批量
LOAD CSV是从 Cypher 里直接读文件的写法,适合几十万行以内、需要做数据清洗和条件判断的场景。文件放在import/目录下,路径就写file:///加相对文件名。
// import/person.csv 表头:id,name,age LOAD CSV WITH HEADERS FROM 'file:///person.csv' AS row MERGE (p:Person {id: row.id}) SET p.name = row.name, p.age = toInteger(row.age); // import/participates.csv 表头:person_id,project_id,role LOAD CSV WITH HEADERS FROM 'file:///participates.csv' AS row MATCH (p:Person {id: row.person_id}) MATCH (pr:Project {id: row.project_id}) MERGE (p)-[r:PARTICIPATES_IN]->(pr) SET r.role = row.role;几个参数和写法上的讲究:WITH HEADERS让每行按表头变成 map,字段名大小写敏感;toInteger()这类转换函数必须显式加,csv 读出来全是字符串,直接比较数字字段会静默不匹配;关系导入用MATCH找两端再MERGE关系,不要用CREATE,重复跑会造出重复边。
数据量大的时候分批跑,别一次性LOAD几百万行,事务太大会吃满堆。
// 用 CALL {} IN TRANSACTIONS 分批提交,每 10000 行一个事务 LOAD CSV WITH HEADERS FROM 'file:///person.csv' AS row CALL { WITH row MERGE (p:Person {id: row.id}) SET p.name = row.name } IN TRANSACTIONS OF 10000 ROWS;IN TRANSACTIONS OF 10000 ROWS是关键,它把一次大事务拆成多个小事务提交,堆占用可控,中途失败也只回滚当前批次。代价是失去整体原子性,导入中断后需要靠幂等的MERGE重跑补齐,所以前面那些唯一约束必须建好。
4.3 百万级以上走 neo4j-admin import
数据量上到百万、千万,LOAD CSV就不合适了,该上离线导入工具。它绕开事务和 Cypher 执行引擎,直接写存储文件,速度快一到两个数量级,代价是只能在空库或指定覆盖的库上跑,不能热导入。
# 停库后执行;--nodes / --relationships 可以写多组 bin/neo4j stop bin/neo4j-admin database import full neo4j \ --nodes=Person=import/person_header.csv,import/person.csv \ --nodes=Project=import/project_header.csv,import/project.csv \ --relationships=PARTICIPATES_IN=import/participates_header.csv,import/participates.csv \ --delimiter=',' \ --array-delimiter=';' \ --overwrite-destination=true \ --skip-bad-relationships=true \ --bad-tolerance=1000 bin/neo4j start参数逐个说清楚:Person=header,csv这种写法是「标签=表头文件,数据文件」,表头文件单独一行定义列名和类型(如id:ID(Person),name:string),比让工具自己猜类型稳;--overwrite-destination=true会清掉目标库现有数据,第一次导入必加,重复导入不加会报库非空;--bad-tolerance允许一定数量的坏行,配合--skip-bad-relationships先把脏数据放过去,导完再看bad.log里到底哪几行有问题。
两种方式的选择,我给个粗线:
| 场景 | 推荐方式 | 原因 |
|---|---|---|
| 十万行以内、需要清洗转换 | LOAD CSV | 能在 Cypher 里做条件判断和函数处理 |
| 几十万行、增量补数 | LOAD CSV+IN TRANSACTIONS | 可重复执行,幂等靠 MERGE |
| 百万行以上、首次建库 | neo4j-admin import | 直写存储,速度快一个量级 |
| 已有库要追加大量数据 | 拆成 CSV 走LOAD CSV | import 工具不支持热导入 |
5. 五个翻车现场:从 OOM 到连接被拒的排查顺序
排 Neo4j 的问题有个基本顺序:先看进程活没活,再看端口通不通,然后看日志最后一屏,最后才怀疑配置和查询。跳过前面几步直接改配置,是最常见的浪费时间方式。下面这几条都是我在真实环境里撞过的,按「现象 → 原因 → 解决」写。
5.1 启动脚本直接退出,日志只有一行 Java 版本错误
现象:执行bin/neo4j start秒退,logs/neo4j.log里写着不支持的 Java 版本或者干脆空文件。
原因:系统默认 java 是 8 或 11,Neo4j 5.24 要求 17 以上。启动脚本按 PATH 找 java,不看系统装了哪些版本。
解决:java -version确认版本,把JAVA_HOME指向 17 或 21 的 JDK,并且写进运行账号的 profile。验证方式是echo $JAVA_HOME在同一个 shell 里能输出正确路径,再启动。
5.2 前台能跑,切后台立刻挂,日志提示打开文件数超限
现象:console模式运行正常,start之后几十秒进程消失,debug.log里有Too many open files。
原因:tar 包没有系统包自带的 systemd 单元,nofile上限沿用的是登录 shell 的默认值(常见 1024),Neo4j 需要几万个文件描述符。
解决:启动前临时ulimit -n 40000,永久生效则写进/etc/security/limits.conf的nofile项,或者用 systemd 托管时在单元文件里写LimitNOFILE=40000。改完重启会话再启动,否则不生效。
5.3 Browser 打不开,或者能开但 Bolt 连不上
现象:http://IP:7474转圈,或者页面能进但应用侧连 7687 报Connection refused。
原因:server.default_listen_address还是默认的127.0.0.1,只监听本机;也可能是防火墙没放行端口。
解决:按 3.2 节改成0.0.0.0或单独指定server.bolt.listen_address,重启后用ss -lntp | grep 7687确认监听地址不是127.0.0.1。如果监听已放开还连不上,再查防火墙和云主机安全组,顺序不要颠倒。
5.4 LOAD CSV 报「找不到文件」,但文件明明在 import 目录
现象:LOAD CSV报Couldn't load the external resource at: file:/xxx.csv,用ls看文件存在。
原因:file:///后面的路径是相对server.directories.import的,不是文件系统绝对路径;也可能是运行账号对文件没有读权限,报错信息不会区分这两种情况。
解决:确认neo4j.conf里server.directories.import的指向,把 CSV 放进去,Cypher 里写file:///person.csv(三个斜杠是协议分隔,后面是相对路径)。权限方面,确保文件属主和运行账号一致,chmod 644给读权限。
5.5 查询越跑越慢,慢查询日志里全是全表扫
现象:数据量到几十万之后,某些按id查节点的 Cypher 从毫秒级涨到几秒,query.log里的elapsedTime一路走高。
原因:没有唯一约束或索引,MATCH (p:Person {id: ...})走的是标签扫描;或者约束建了但状态还在POPULATING,没到ONLINE。
解决:SHOW CONSTRAINTS和SHOW INDEXES确认状态,补建约束并等它ONLINE。然后用PROFILE跑一遍目标查询,看执行计划里是NodeByLabelScan还是NodeIndexSeek——前者说明索引压根没用上。这一步比盲目调内存有用得多。
6. 备份、执行计划与几个值得固化的习惯
社区版没有企业版那种在线热备,但neo4j-admin提供的停机 dump/load 完全够用,前提是你把它写进例行流程,而不是等出事再想起来。备份这件事最怕「知道怎么做但从来没跑通过一次」,真到要恢复的时候才发现路径写错、权限不够、文件不完整。
6.1 停机备份与恢复的完整链路
# 备份:必须停库,社区版不支持在线 dump bin/neo4j stop bin/neo4j-admin database dump neo4j --to-path=/data/backup --verbose bin/neo4j start # 恢复:--overwrite-destination 会覆盖同名库,先确认目标 bin/neo4j stop bin/neo4j-admin database load neo4j --from-path=/data/backup --overwrite-destination=true bin/neo4j start # 验证恢复结果:节点数、关系数、关键约束是否还在 bin/cypher-shell -u neo4j -p 'YourStrongPassw0rd' \ "MATCH (n) RETURN count(n) AS nodes;"--to-path指定的目录必须存在且运行账号可写,dump 出来是单个.dump文件,可以直接拷走归档。恢复时--overwrite-destination=true是必要的,不加会因目标库已存在而失败。恢复完别急着宣布成功,跑一遍节点计数和SHOW CONSTRAINTS,确认约束和索引都在——dump 文件包含约束定义,但索引需要重建,状态可能短暂是POPULATING。
6.2 用 EXPLAIN 和 PROFILE 验证索引真的被用上
EXPLAIN只看计划不执行,PROFILE会真跑一遍并给出每个算子的实际行数和 db hits。调优时我基本只用PROFILE,因为计划里写着NodeIndexSeek但实际 db hits 很高的情况并不少见,那说明索引选错了或者过滤条件没下推。
// 只看执行计划,不产生实际执行开销 EXPLAIN MATCH (p:Person {id: 'P10086'}) RETURN p; // 真跑并统计,重点看 db hits 和 rows PROFILE MATCH (p:Person {id: 'P10086'})-[:PARTICIPATES_IN]->(pr:Project) RETURN pr.name;判断标准很朴素:NodeByLabelScan出现在大标签上就是问题,NodeIndexSeek加Expand(All)是正常形态;db hits远大于返回行数,说明有算子在重复扫。改完之后再PROFILE一次对比,别凭感觉说「快了」。
6.3 一个被我固化下来的检查清单
从那以后我每次部署新的 tar 包实例,都会强制走一遍这个顺序:java -version确认 17/21、ulimit -n确认 40000、server.memory.heap.initial_size与max_size是否相等、server.default_listen_address是否符合暴露边界、bin/neo4j console前台起一次看首屏日志、SHOW CONSTRAINTS确认ONLINE、最后跑一次 dump 和 load 验证备份链路通。这七步花不到二十分钟,能挡掉后面八成以上的返工。
tar 包这种形态给了你全部控制权,代价是所有默认值都得自己确认一遍。这套习惯不是从文档里抄的,是几次半夜被叫起来看「库连不上」之后一点点攒出来的,希望对你有用。资源就在那里,解压、核 Java、改三处配置、建约束、灌数,一条链路走通,后面就是业务的事了。希望帮到你。
本文还有配套的精品资源,点击获取