简介:Neo4j 社区版 5.23.0 Windows 压缩包面向图数据库入门者、后端开发人员与数据工程从业者,用于在本地快速搭建可运行的图数据存储、关系建模与查询分析环境。压缩包共264个文件,约119.26MB,以235个jar依赖库为主,并含bat与ps1启动脚本、conf配置、exe服务组件及txt说明,覆盖命令行工具、管理模块、浏览器可视化与证书配置,目录按功能划分便于检索。已有527人学习下载,适合课程实验、原型验证与Cypher查询练习;包内脚本和配置可帮助读者完成服务启停、数据导入导出、权限与证书设置,并结合节点、关系、属性等图形模型理解复杂关联查询和数据可视化流程。对理解图遍历、路径查找、多跳关系分析及图数据库存储结构与查询执行过程也有帮助,适合从单机试用过渡到项目实践。
1. 一个解压即用的图数据库:Neo4j 社区版 Windows 包解决什么问题
在关系型数据库里把三层自关联的 JOIN 写成 CTE、跑出四十秒,我就知道问题不在 SQL 写得丑,而在数据本身就是图。换 Neo4j 数据库的成本比我预想的低——社区版的 Windows 压缩包不写注册表、不弹安装向导,解压到一个没有中文、没有空格的目录就能起来,不用了直接删目录。这个 neo4j-chs-community-5.23.0-windows.zip 就是这样的东西:5.23.0 社区版、Windows 平台、文件名里的 chs 一般指带了中文界面资源,免安装。
它适合三类人:想在定架构之前先把 Cypher 和图建模跑通的;内网、离线环境里走不了在线安装的;以及要在同一台机器上并存两三个版本做兼容对比的。它的边界也要先讲明白:社区版没有集群、没有在线热备份,生产高可用别指望它,拿它做建模验证、离线工具链、教学演示和中小规模单机服务才是正解。
2. 解压到首次启动:目录、JDK 17 硬门槛与五个必改配置
2.1 先认这七个目录,再决定放哪
把 zip 解开之后别急着双击脚本,先看一眼结构。这套布局从 4.x 到 5.x 基本没变,认得一次后面所有小版本都能套。
| 目录 | 作用 | 我的改动频率 |
|---|---|---|
bin | neo4j.bat、cypher-shell.bat、neo4j-admin.bat | 不改,只用 |
conf | neo4j.conf是唯一配置入口 | 每次部署都改 |
data | 图存储文件、事务日志、检查点 | 只备份,绝不手改 |
import | LOAD CSV里file:///的根目录 | 常往里丢文件 |
logs | neo4j.log、debug.log、http.log | 排错第一现场 |
plugins | APOC 之类的 jar 放这里 | 用到才动 |
licenses | 许可文本 | 不动 |
解压路径比目录结构更能决定成败。常见问题有三个:放C:\Program Files下,装成服务后服务账户没有写权限,日志会莫名写进VirtualStore里,你会找不到它们;路径带空格,某些脚本里没加引号的参数会断成两截;放在 OneDrive、坚果云这类同步目录里,同步进程会周期性锁data目录下的文件,前台启动时报Unable to lock store。我一般固定在D:\neo4j\neo4j-community-5.23.0这种短路径下,多个版本就并排建-5230、-5240的兄弟目录。
2.2 JDK 17 是硬门槛,JAVA_HOME比PATH更决定生死
Neo4j 5.x 系列要求 JDK 17,启动脚本会先做版本检查,低于 17 直接拒绝启动,不会给你一个含糊的报错。麻烦的是很多人的机器上同时装着 JDK 8 和 17,PATH里谁排前面完全是历史遗留,光java -version看着是 17 不代表脚本拿到的是 17。
稳妥做法是显式指定JAVA_HOME,而不是赌PATH顺序:
:: cmd 窗口里临时指定,只影响当前窗口,最安全 set JAVA_HOME=D:\jdk\jdk-17 "%JAVA_HOME%\bin\java.exe" -version :: 想以后长期生效,用 setx,但要重开窗口才读得到 setx JAVA_HOME "D:\jdk\jdk-17"# PowerShell 下的等价写法,只影响当前会话 $env:JAVA_HOME = "D:\jdk\jdk-17" & "$env:JAVA_HOME\bin\java.exe" -version-version的输出里必须能看到17.x。如果启动时提示找不到合适的 JVM,或者日志里出现Unsupported class file major version,八成就是这里:脚本拿到的还是 JDK 8。另一个隐蔽情况是 JDK 是绿色解压版、JAVA_HOME指向了bin的上一层而不是 JDK 根目录,bin\neo4j.bat会去找%JAVA_HOME%\bin\java.exe,路径差一层就找不到。改完环境变量记得关掉旧 cmd 窗口重新开,老窗口里的环境变量是启动时快照,不会跟着变。
2.3 前台console起一次,再用 Windows 服务常驻
第一次启动一定用前台模式,让日志直接刷在屏幕上,出问题第一眼就能看见:
cd /d D:\neo4j\neo4j-community-5.23.0 bin\neo4j.bat console刷到Started.就说明 Bolt(7687)和 HTTP(7474)都起来了。这个窗口不能关,Ctrl+C就是停止。验证用自带的命令行客户端:
bin\cypher-shell.bat -a bolt://127.0.0.1:7687 -u neo4j -p neo4j首次登录会强制改密码,新密码至少 8 位,改完才算真正进到交互环境。进去之后先跑三句确认基本盘:
RETURN 1; SHOW DATABASES; CALL dbms.components() YIELD name, versions, edition RETURN name, versions, edition;第三句能确认版本号和 edition 是community,避免下错包。退出用:exit。
确认没问题再装成服务常驻:
:: 必须以管理员身份打开 cmd bin\neo4j.bat install-service bin\neo4j.bat start bin\neo4j.bat status :: 不想用了先停再卸,否则服务项会留在系统里 bin\neo4j.bat stop bin\neo4j.bat uninstall-service注意,服务方式跑起来之后,工作目录和你手动cd的那个目录不一定一致,import的相对路径也可能对不上。另外多版本并存时不要同时装成服务,默认服务名会撞,只保留一个做服务、其余全用console起,是最省事的分法。
2.4neo4j.conf里最该先动的五个键
配置文件在conf\neo4j.conf,改完必须重启才生效。下面五个键覆盖了本地开发和受控内网两种场景的绝大部分需求:
# 只监听本机就把 0.0.0.0 换成 127.0.0.1 server.default_listen_address=0.0.0.0 server.bolt.listen_address=:7687 server.http.listen_address=:7474 # 本地开发没必要开 HTTPS,自签证书会被浏览器拦 server.https.enabled=false # 默认数据库名,cypher-shell 不带 -d 时连的就是它 initial.dbms.default_database=neo4jserver.default_listen_address是总开关,设成0.0.0.0意味着同网段其他机器能连进来。这一点要格外小心:默认账号加全监听,是最经典的坑。真要开,先把密码改成强口令,再确认防火墙只放行可信网段。改成127.0.0.1之后,本机浏览器和cypher-shell照常能用,只是外部连不进来。
端口冲突的处理顺序是:先netstat -ano | findstr :7687找到占用进程,再决定是杀掉还是改端口。改端口要成对改,server.bolt.listen_address=:7688之后,cypher-shell的-a参数、你所有客户端代码里的连接串都得跟着改,漏一个就白排查半小时。
3. 建模与批量导入:约束、LOAD CSV、Python driver 三条路
3.1 约束一定建在灌数据之前
5.x 的约束语法和 4.x 不一样,网上抄来的老语句会直接报语法错。现在的写法是把约束名放在FOR前面:
// 5.x 语法:CREATE CONSTRAINT <名字> FOR <模式> REQUIRE ... CREATE CONSTRAINT person_id IF NOT EXISTS FOR (p:Person) REQUIRE p.personId IS UNIQUE; CREATE INDEX person_name IF NOT EXISTS FOR (p:Person) ON (p.name); // 建完立刻验证,别靠记忆 SHOW CONSTRAINTS YIELD name, type, labelsOrTypes, properties; SHOW INDEXES YIELD name, state, type, entityType, labelsOrTypes;顺序为什么重要:唯一约束本身会连带建一个索引,后面成千上万条MERGE (p:Person {personId: ...})才能走索引查而不是全图扫。要是先灌了十万行再补约束,建约束那一步要做全量扫描加去重校验,大库上可能几十分钟起步,中途失败还得回滚。IF NOT EXISTS是幂等保护,重复执行不报错,适合写进初始化脚本。约束名建议带上标签名和字段名,等你库里有二三十条约束的时候,SHOW CONSTRAINTS里一堆默认名会让人抓狂。
SHOW INDEXES输出的state列要关注,正常是ONLINE,如果是POPULATING说明还在建索引,这时候跑查询会走全表扫,慢得莫名其妙。
3.2LOAD CSV:file:///指向的其实是import目录
这是新手第一个卡点。file:///persons.csv里的三斜杠不是笔误,也不是绝对路径,它相对的是conf里server.directories.import配置的目录,默认就是解压目录下的import。把 CSV 丢进import,写file:///persons.csv就对了。
LOAD CSV WITH HEADERS FROM 'file:///persons.csv' AS row FIELDTERMINATOR ',' CALL { WITH row MERGE (p:Person {personId: row.person_id}) SET p.name = row.name, p.city = row.city } IN TRANSACTIONS OF 5000 ROWS;逐个参数说清楚:WITH HEADERS让第一行变成字段名,不加的话只能用row[0]、row[1]这种下标访问,改列顺序就全乱;FIELDTERMINATOR默认是逗号,读 TSV 要写成'\t';CALL { ... } IN TRANSACTIONS OF 5000 ROWS是 5.x 里把大文件切成小事务的标准手法,每 5000 行提交一次,内存占用可控,中途失败也只丢最后一个批次。不切批的话,一个几十万行的文件会整成一个事务,很容易撞上内存上限或者db.transaction.timeout。
远程文件也能读,但开关默认是关的:
# 允许 LOAD CSV 直接读 http(s) 地址,默认 false dbms.security.import.remote_url.enabled=true打开之前想清楚:这是让数据库进程主动往外发请求,内网环境里通常保持关闭,改成先下载到import再导入。
编码问题比路径问题更隐蔽。Windows 上 Excel 另存 CSV 默认是 GBK 或者带 BOM 的 UTF-8,Neo4j 按 UTF-8 读,中文列会出现乱码或首列名带一个看不见的\ufeff。判断方法:如果第一列的字段名怎么都对不上,先怀疑 BOM。处理办法是用记事本或 VS Code 另存为「UTF-8 无 BOM」,或者用 Python 读进来重新写一遍。
3.3 Python driver:execute_query加批量UNWIND
要往库里写的场景比读多,LOAD CSV适合一次性迁移,日常接入还是走 Bolt。驱动 5.x 之后execute_query已经是很顺手的入口,自动管理会话和事务:
from neo4j import GraphDatabase import csv URI = "bolt://127.0.0.1:7687" AUTH = ("neo4j", "换成你自己的密码") BATCH = 2000 # UNWIND 把一批行当参数传进去,一条语句写一批 QUERY = """ UNWIND $rows AS row MERGE (p:Person {personId: row.person_id}) SET p.name = row.name, p.city = row.city """ def chunks(seq, n): for i in range(0, len(seq), n): yield seq[i:i + n] with GraphDatabase.driver(URI, auth=AUTH) as driver: driver.verify_connectivity() # 连不上在这里就炸,别等到写一半 with open("persons.csv", newline="", encoding="utf-8") as f: rows = [{"person_id": r["person_id"], "name": r["name"], "city": r["city"]} for r in csv.DictReader(f)] for batch in chunks(rows, BATCH): driver.execute_query(QUERY, rows=batch, database_="neo4j")逻辑说明:UNWIND $rows把整批数据当成一个列表参数,服务端只编译一次执行计划,比在 Python 里循环发两千条语句快一个数量级。BATCH = 2000是经验值,太小网络往返开销占比高,太大单个事务的内存和持锁时间都上去了。driver.verify_connectivity()放在最前面,连不上就在这里直接报错,不会出现写了一半才发现端口写错的情况。with语句保证 driver 关闭,漏了会残留连接把连接池占满。
重试要谨慎。execute_query默认对可重试错误做有限重试,MERGE加SET这种幂等写法重试安全,CREATE不幂等,重试一次就多一条重复数据。要么统一用MERGE加唯一约束兜底,要么显式把重试时间设成 0。
3.4 用PROFILE看 db hits,别用EXPLAIN猜
EXPLAIN只编译不执行,看不到真实代价;PROFILE会真跑一遍,把每个算子的db hits和输出行数都打出来。调优看这两个数就够了:db hits高说明扫得多,实际行数和预期差太远说明过滤条件没走索引。
PROFILE MATCH (p:Person {city: $city})-[:KNOWS*1..2]->(f:Person) RETURN f.name LIMIT 20;看计划里第一个算子:是NodeIndexSeek就对了,是AllNodesScan说明city上没有索引,去CREATE INDEX补一个。变长路径*1..2在稠密图上是指数级展开,LIMIT 20限制的只是返回行数,不减少展开量,二跳在平均度数上百的图上就能把内存吃穿。真要做多跳可达性,限制最大深度、加关系类型过滤,或者上 APOC 的路径扩展过程,别裸写变长模式。
4. 内存与页缓存:5.23 上必须自己算的四个参数
4.1 heap 和 pagecache 的分配比例
Neo4j 的内存模型比关系库简单,就两块:堆内存放查询执行、事务状态、锁表;页缓存把磁盘上的图记录映射进内存,命中就不走磁盘。两块加起来不能把物理内存吃光,操作系统和文件系统缓存还要留位置,我一般控制在物理内存的 70%~80%。
# 初始值和最大值设成一样,避免运行中扩堆触发 Full GC 停顿 server.memory.heap.initial_size=4g server.memory.heap.max_size=4g server.memory.pagecache.size=8g估算顺序是反过来的:先看data\databases\neo4j目录有多大,页缓存尽量把它装下,装不下就按可用内存尽量给,剩下的靠磁盘;堆从 4G 起,查询里大量collect、ORDER BY、路径展开再往上加。堆超过 32G 会丢掉压缩指针,同样的对象占更多内存,反而不划算,这是血泪经验。
页缓存不足的典型症状:db hits不算高但查询就是慢,任务管理器的磁盘活动率常年打满。这时候加堆没用,必须加页缓存。反过来,堆不够的症状是OutOfMemoryError、GC 日志里 Full GC 频繁,这时候加页缓存同样没用。
4.2 事务日志与检查点:磁盘被悄悄吃满的根源
写操作先进事务日志,检查点把内存里的脏页刷进存储文件,刷完之后旧日志才能被回收。回收策略配错,批量导入几百万行能把磁盘写满。
# 单个日志文件滚动阈值 db.tx_log.rotation.size=256M # 保留策略:时间 + 总量双上限,两个条件谁先到按谁删 db.tx_log.rotation.retention_policy=2 days 1G # 检查点触发条件:时间和事务数 db.checkpoint.interval.time=20m db.checkpoint.interval.tx=100000retention_policy里只写时间不写总量是很常见的失误:某天批量导入写了 30G 日志,策略说保留 7 天,于是这 30G 就老老实实躺满 7 天。加上总量上限之后,超了会被更积极地回收。检查点间隔也别调太大,间隔越长,崩溃恢复时要重放的日志越多,启动越慢。
4.3 用dbms.listConfig确认参数真的生效
改完neo4j.conf一定要重启,然后查一遍实际生效值,别信配置文件本身:
CALL dbms.listConfig() YIELD name, value, isDynamic WHERE name STARTS WITH 'server.memory' OR name CONTAINS 'checkpoint' OR name CONTAINS 'tx_log' RETURN name, value, isDynamic ORDER BY name;isDynamic这一列告诉你这个参数能不能在线改。5.x 提供了一部分动态配置能力,但内存、端口、监听地址这类跟进程启动绑定的只能重启。查出来的值和配置文件里写的不一致,优先怀疑三件事:配置项名字拼错(拼错的项会被忽略,不报错)、写在了注释块里、以及有第二份配置在起作用(比如服务安装时带了额外的 conf 路径)。
4.4 压测时盯住这四个指标
| 指标 | 查看方式 | 正常表现 | 异常信号 |
|---|---|---|---|
| 页缓存命中率 | CALL dbms.queryJmx('org.neo4j:instance=kernel#0,name=Page cache') | 命中率高、faults 增长平缓 | faults 持续飙升,说明缓存装不下 |
| 活跃与阻塞事务 | SHOW TRANSACTIONS YIELD transactionId, currentQuery, elapsedTime, status | 事务生命周期短 | elapsedTime长时间不降,有长事务 |
| 索引状态 | SHOW INDEXES YIELD name, state, populationPercent | 全部ONLINE | 卡在POPULATING |
| 磁盘活动率 | Windows 性能监视器看PhysicalDisk | 空闲时接近 0 | 查询期间常年 90% 以上 |
SHOW TRANSACTIONS是排查「数据库突然变慢」的第一站。看到一条elapsedTime已经几分钟的写事务,基本就是它把锁占着,其他写全在排队。确认之后要么等,要么在确认业务影响之后终止它。
5. 避坑与排查:五类现场记录和它们的根因
5.1 启动阶段:闪退、端口占用
现象:双击bin\neo4j.bat,黑窗口一闪就没了,什么也没看到。原因:脚本在检查阶段就退出了,最常见是 JDK 版本不对或者JAVA_HOME指向错误;也可能是解压路径里有中文或空格,脚本解析路径时断掉。解决:不要双击,用管理员 cmd 进去手动执行bin\neo4j.bat console,错误信息会留在窗口里。先跑echo %JAVA_HOME%和"%JAVA_HOME%\bin\java.exe" -version确认 JDK,再把目录换到纯英文短路径下重试。
现象:日志里Address already in use: bind,或者Component 'org.neo4j.server.CommunityNeoServer' failed to start。原因:7687 或 7474 被占用。多半是上一次的进程没退干净(服务模式和 console 模式各起了一个),或者本机还有另一个图数据库工具占了同一个端口。解决:netstat -ano | findstr :7687拿到 PID,tasklist | findstr <PID>确认是什么进程,是残留的 java 进程就taskkill /PID <PID> /F;是别的软件就改 Neo4j 的端口,并且把所有客户端连接串一起改。
5.2 连接与认证:连不上、密码忘了
现象:cypher-shell报Neo.ClientError.Security.Unauthorized,但账号密码明明没打错。原因:第一次用默认密码neo4j登录时会被强制要求改密码,改密流程没走完,或者后来改过一次自己忘了。另一种情况是密码长度不满足 8 位要求,改密那一步直接失败。解决:先用默认密码连一次,跟着提示走完改密;彻底忘了就停库删掉data\dbms\auth相关文件重置(等于清空所有用户),这是离线包相对在线服务的一个便利。生产数据目录下做这件事之前先整目录备份。
现象:浏览器打开http://127.0.0.1:7474一直转圈,但cypher-shell用 bolt 能连上。原因:server.http.listen_address被改成了127.0.0.1之外的值又没配对,或者server.https.enabled=true导致 HTTP 端口变成跳转,或者 Windows 防火墙拦了 7474。解决:先看logs\neo4j.log里 HTTP 服务实际绑定到哪个地址端口,再对照conf里的配置,最后确认防火墙入站规则。本地开发直接把 https 关掉、监听地址设127.0.0.1最省事。
5.3 导入与运行:文件读不到、事务撑爆
现象:LOAD CSV报Unable to load from file 'file:///xxx.csv',文件确实存在。原因:三种可能——文件没放进import目录;路径里用了反斜杠\;文件名带中文或空格。file:///后面跟的路径要求正斜杠。解决:把文件挪进import目录,语句里统一写file:///name.csv,中文文件名改成英文短名。要读import之外的位置,得在conf里放开server.directories.import的限制,而不是硬改路径写法。
现象:导入跑到一半报OutOfMemoryError或者事务超时,重跑又得从头来。原因:整个 CSV 被当成一个事务,中间状态全在堆里,几十万行就把堆撑满;或者单条MERGE没有唯一约束兜底,变成全图扫。解决:用CALL { ... } IN TRANSACTIONS OF 5000 ROWS切批,先建唯一约束再导,db.transaction.timeout按批大小调整而不是无限放大。切批之后即使失败,重新导入也是幂等的,MERGE不会产生重复节点。
6. 备份与迁移:社区版没有在线备份,怎么把 zip 用成可搬走的环境
社区版和商业版在运维能力上最大的差别就在这里:在线热备份是商业版功能,社区版只能用离线工具。认清这一点,反而能把流程理得很干净——备份就是停库、dump、启库,三步固定动作。
:: 1. 先停库,dump 要求数据库处于一致状态 bin\neo4j.bat stop :: 2. 导出到指定目录,-verbose 会把进度打出来 bin\neo4j-admin.bat database dump neo4j --to-path=D:\backup --verbose :: 3. 恢复(会覆盖目标库,务必先改名留底) bin\neo4j.bat stop bin\neo4j-admin.bat database load neo4j --from-path=D:\backup --overwrite-destination=true bin\neo4j.bat start几种搬迁方式的边界要分清,选错了就是几个小时白干:
| 方式 | 是否需要停库 | 适用场景 | 主要限制 |
|---|---|---|---|
database dump加load | 需要 | 同大版本内迁移、定期归档 | 跨大版本要先按升级路径来 |
直接拷贝data目录 | 需要 | 整机搬迁、同版本冷备 | 目录结构不兼容时直接起不来 |
neo4j-admin database import full | 需要(空库) | 从 CSV 首次建库 | 只适合初始导入,不能增量 |
| 写 Cypher 导出脚本 | 不需要 | 小数据量、结构也要一起带走 | 大数据量非常慢,索引要重建 |
跨大版本升级别想省事。小版本之间换掉bin、lib、plugins里的二进制,保留data和conf,一般能直接起;跨大版本时存储格式会变,先在新版本的空库上跑import或dump/load,再把业务验证一遍,比事后修数据便宜得多。搬迁前还有一件容易漏的事:plugins目录里的 APOC 之类插件不会跟着data走,新环境要单独放,并且确认dbms.security.procedures.allowlist里放开了对应过程,否则过程调用会报找不到。
从那以后我每次拿到一个新的 zip 包,都强制走一遍四步验证:console起得来、默认密码改得掉、唯一约束建得上、dump之后能load回来。四步全绿才敢往里灌真实数据,缺一步后面都可能变成半夜的排查。希望帮到你。
本文还有配套的精品资源,点击获取