4.1 官方驱动与连接管理:三层对象的生命周期 本节摘要:官方驱动把"发查询"这件小事包进了三层对象:Driver 管连接池、Session 管一次逻辑交互、Transaction 管原子边界。本节以 Python 驱动为主线,讲连接参数、事务函数与自动重试、书签因果链三件核心技能,并给出连接失败的排查清单。掌握这一节,任何语言的驱动都只是换个语法。 第 3 章的事务纪律(短小、幂等、可重试)在本节变成代码。先建立三层对象的心智图,再逐层落地。 一、三层对象:Driver、Session、Transaction Driver 是进程级单例:内部维护 Bolt 连接池,负责路由(单机直连,集群按角色路由)、认证与加密。
本节摘要:官方驱动把"发查询"这件小事包进了三层对象:Driver 管连接池、Session 管一次逻辑交互、Transaction 管原子边界。本节以 Python 驱动为主线,讲连接参数、事务函数与自动重试、书签因果链三件核心技能,并给出连接失败的排查清单。掌握这一节,任何语言的驱动都只是换个语法。
第 3 章的事务纪律(短小、幂等、可重试)在本节变成代码。先建立三层对象的心智图,再逐层落地。
Driver 是进程级单例:内部维护 Bolt 连接池,负责路由(单机直连,集群按角色路由)、认证与加密。应用生命周期内创建一次,退出时关闭——反复创建 Driver 是最常见的性能反模式。
# Python:进程级单例,退出时关闭 from neo4j import GraphDatabase driver = GraphDatabase.driver( "bolt://localhost:7687", # 集群写 neo4j+s://host:7687 加密路由 auth=("app-server", "secret"), max_connection_pool_size=50, # 连接池上限,按并发调 ) ... driver.close()
Session 是一次逻辑交互的容器(通常对应一个请求),Transaction 是原子边界:
# 一次请求 = 一个 session;一次原子操作 = 一个托管事务 def add_friend(tx, a, b): tx.run(""" MERGE (x:Person {name: $a}) MERGE (y:Person {name: $b}) MERGE (x)-[:KNOWS]->(y) """, a=a, b=b) with driver.session(database="neo4j") as s: s.execute_write(add_friend, "Alice", "Bob")
execute_write 是托管事务:回调里的全部语句共用一个事务,提交失败自动重试——重试安全的前提就是 3.1 讲过的 MERGE 幂等。三层职责一句话:Driver 活全程,Session 活请求,Transaction 活原子操作。
驱动对"可重试"错误(死锁、主库切换、连接抖动)内置指数退避重试,默认策略已合理。需要调整时按业务改超时:
# 自定义重试窗口:整体不超过 15 秒 import neo4j retry_config = neo4j.RetryLogic() if hasattr(neo4j, "RetryLogic") else None # Python 驱动 5.x 直接由 execute_query 内建策略接管,Java 驱动可显式构造: # TransactionRetryLogic().maxRetryTime(15, SECONDS).build() result = driver.execute_query( "MATCH (p:Person {name: $n}) RETURN p.born AS born", n="Tom Hanks", database_="neo4j", ) print(result.records[0]["born"]) # 1956
死锁回放(3.2 节的场景): 事务A/B 冲突 → 一个收到 TransientError → execute_write 捕获 → 指数退避后重发 → 第二次成功 → 应用层看不到任何异常
⚠️ 重试的前提是回调幂等。回调里若有"自增计数""append 日志"这类非幂等操作,重试会放大副作用——把这类逻辑移出事务回调。
集群里读走从库(最终一致),"刚写完立刻读"可能读到旧数据。书签机制把写事务结束的因果点传给读会话:
# 写事务结束后取书签 with driver.session(database="neo4j") as s: s.execute_write(add_friend, "Alice", "Bob") bookmark = s.last_bookmarks() # 下一次读会话携带书签:驱动保证去"已包含该写入"的服务器上读 with driver.session(database="neo4j", bookmarks=bookmark) as s: n = s.execute_read(lambda tx: tx.run( "MATCH (:Person {name: 'Alice'})-[:KNOWS]->() RETURN count(*) AS c" ).single()["c"]) print(n) # 1 —— 不会读到旧数据
单机部署感受不到书签的价值;一旦集群读从库,它是"注册成功却查不到自己"这类工单的标准解药。5.2 节讲集群路由时会回到这里。
应用连不上库,90% 落在四件事上,按序排查:
1. 地址与协议:单机 bolt://,集群 neo4j://;端口 7687(不是 7474) 2. 认证:账号密码是否对;密码强制更换未完成会拒连 3. 加密:默认策略与服务端加密设置是否匹配(本地开发先关加密最快定位) 4. 路由:集群部署用 neo4j:// 协议,让驱动拿到完整路由表; 只给一个从库地址的 bolt:// 直连,主库切换后就会假死

驱动选型基本跟随团队主语言,四门官方驱动的性格差异:
| 驱动 | 性格 | 适合 |
|---|---|---|
| Java | 最成熟,性能天花板最高 | 大型后端、Spring 生态 |
| Python | API 最简洁,数据科学生态近 | 脚本、数据管道、快速原型 |
| JavaScript/TypeScript | 异步模型贴合 Node | 全栈 Web、服务端渲染 |
| Go / .NET | 各自生态内标准形态 | 微服务、微软技术栈 |
同一套三层对象心智在四者间平移,选型失误的成本几乎为零——别为驱动纠结,为建模花时间。
工具是人的交互界面。下一节看 Browser 与 cypher-shell 怎么配合。