摘要 :本文记录了一次完整的图数据库性能基准测试实验,聚焦**语义搜索(向量相似度检索)**场景,测试对象为 Neo4j 5.26、FalkorDB 4.16 和 Memgraph 3.0。测试基于 10 万节点 + 50 万边的社交网络合成图,向量维度 64 维,涵盖向量 KNN、混合过滤、图+向量联合查询等典型场景。结论先行:FalkorDB 延迟最低(0.3ms),比 Neo4j 快 9×、比 Memgraph 快 2.8×,但 Neo4j 功能最完整,Memgraph 加载最快。
一、为什么测这三个图数据库?
近年来随着大模型(LLM)和 RAG(检索增强生成)应用的兴起,向量检索与图结构检索的融合成为一个重要技术方向。典型场景如知识图谱 + 向量检索:用户输入自然语言查询 → 转换为向量 → 在图数据库中找到语义最相近的实体节点 → 再通过图关系扩展查找相关实体 → 返回给 LLM 生成答案。
这就对图数据库提出了新的要求:不仅要支持图遍历,还要内置向量索引(HNSW/IVF 等)并支持"向量 KNN + 属性过滤 + 图扩展"的复合查询。
在众多图数据库中,我重点关注了三款:
| 数据库 | 类型 | 向量搜索支持 | 许可证 |
|---|---|---|---|
| Neo4j 5.26 | 成熟商业/开源图数据库 | 原生向量索引(5.11+) | 社区版 GPL |
| FalkorDB 4.16 | 基于 Redis Module 的图数据库 | 原生向量索引(HNSW) | RSALv2/SSPL |
| Memgraph 3.0 | 内存图数据库 | 原生向量索引(USearch,3.0+) | BSL |
选择这三个的原因:
- Neo4j 是事实标准,参考基线
- FalkorDB 基于 Redis,号称极低延迟
- Memgraph 是近年冒头的高性能内存图数据库,支持流式处理
二、测试环境搭建(踩坑实录)
2.1 最初计划:Docker 部署
最初计划用 Docker Compose 一键启动三个数据库,配置如下:
yaml
version: "3.8"
services:
neo4j:
image: neo4j:5.26.0-community
ports: ["7474:7474", "7687:7687"]
environment:
- NEO4J_AUTH=neo4j/benchmark123
falkordb:
image: falkordb/falkordb:latest
ports: ["6379:6379"]
memgraph:
image: memgraph/memgraph-mage:latest
ports: ["7688:7687"]
然而,服务器网络环境对 Docker Hub 有封锁,所有镜像拉取均超时,尝试:
- 配置国内镜像源(USTC/163/百度)→ 镜像本身不可达
- 修改
/etc/hosts手动映射 Docker Hub IP → SSL 证书 SNI 不匹配 - 配置 DNS 8.8.8.8 → Cloudflare CDN 层仍被拦截
最终判断:Docker 方案不可行,改为原生安装。
2.2 原生安装各数据库
Neo4j 5.26
bash
# apt 仓库的 neo4j 2026.x 需要 Java 25,系统只有 Java 21,只能手动下载
wget https://dist.neo4j.org/neo4j-community-5.26.0-unix.tar.gz
tar -xzf neo4j-community-5.26.0-unix.tar.gz -C /data/graph-db-benchmark/
FalkorDB 4.16(作为 Valkey 模块加载)
Ubuntu 24.04 已将 Redis 替换为 Valkey:
bash
apt install valkey-server # 不再有 redis-server
wget https://github.com/FalkorDB/FalkorDB/releases/download/v4.16.9/falkordb-x64.so
chmod +x /data/falkordb-x64.so
# 启动时加载模块
valkey-server --loadmodule /data/falkordb-x64.so --port 6379 --protected-mode no
Memgraph(踩坑最多)
Memgraph 安装后默认端口 7687 与 Neo4j 冲突,修改配置为 7688:
--bolt-port=7688
--data-directory=/data/graph-db-benchmark/volumes/memgraph/data
三、测试数据集设计
3.1 合成社交网络图
测试数据为一个模拟社交网络:
python
# 10万用户节点,每个用户有:
{
"id": 0~99999,
"name": "随机英文姓名", # Faker 生成
"age": 18~75,
"country": "从15个国家随机选取",
"email": "随机邮箱",
"emb": [64维 float32 向量] # 语义搜索专用
}
# 50万条 FOLLOWS 关系(幂律分布,模拟真实社交网络)
3.2 向量嵌入生成
为了模拟真实语义搜索场景,给每个节点附加一个 64 维 L2 归一化向量(模拟 sentence embedding):
python
import numpy as np
# 生成 10 万个 64 维归一化向量(固定随机种子,可复现)
rng = np.random.default_rng(seed=42)
embeddings = rng.standard_normal((100_000, 64)).astype(np.float32)
norms = np.linalg.norm(embeddings, axis=1, keepdims=True)
embeddings = embeddings / norms # L2 归一化,cosine 相似度 = dot product
# 生成 500 个查询向量(固定种子 99)
query_vectors = generate_embeddings(500, 64, seed=99)
64 维的选择权衡了真实性(sentence-transformers 常用 384/768 维,但 64 维已能体现向量检索的关键性能特征)和测试速度。
四、测试方法设计
4.1 核心原则:顺序隔离测试
不能同时跑多个数据库! 早期测试发现并发运行会相互竞争 CPU/内存,导致结果失真。因此设计为:
每个数据库独立测试周期:
1. 停止其他所有数据库
2. 启动当前数据库
3. 清空数据 → 导入测试数据(含向量)
4. Phase A:无索引基线测试
5. 创建向量索引(记录构建时间)
6. Phase B:有索引场景测试
7. 停止当前数据库
4.2 测试查询设计(6 类)
| 编号 | 查询名 | 描述 | 目的 |
|---|---|---|---|
| Q1 | vec_brute_knn | 全表扫描余弦相似度 KNN(无索引) | 展示索引必要性 |
| Q2 | vec_idx_knn | 向量索引 KNN,k=10 | 核心语义搜索延迟 |
| Q3 | vec_idx_filtered | 向量 KNN k=50 + country 属性过滤 | 带条件的语义搜索 |
| Q4 | vec_graph_expand | 向量 KNN k=5 → 展开 1 跳图邻居 | 图+向量混合查询 |
| Q5 | text_scan | CONTAINS 文本扫描(无索引) |
文本搜索基线 |
| Q6 | text_ft_idx | 全文索引搜索 | 生产级文本搜索 |
4.3 各数据库向量索引 API
三个数据库向量索引的创建和查询语法差异较大:
Neo4j 5.26(向量索引成熟,自 5.11 版本起支持):
cypher
-- 创建向量索引
CREATE VECTOR INDEX user_emb_idx IF NOT EXISTS FOR (n:User) ON (n.emb)
OPTIONS {indexConfig: {`vector.dimensions`: 64, `vector.similarity_function`: 'cosine'}}
-- KNN 查询
CALL db.index.vector.queryNodes('user_emb_idx', 10, $vec) YIELD node, score
RETURN node.name, score
-- 向量 + 属性过滤(先取 top50,再过滤)
CALL db.index.vector.queryNodes('user_emb_idx', 50, $vec) YIELD node, score
WHERE node.country = $country
RETURN node.name, score LIMIT 10
-- 向量 + 图扩展
CALL db.index.vector.queryNodes('user_emb_idx', 5, $vec) YIELD node, score
MATCH (node)-[:FOLLOWS]->(nbr:User)
RETURN nbr.name, score ORDER BY score DESC LIMIT 20
FalkorDB 4.16 (使用专用 vecf32 类型):
cypher
-- 节点属性必须使用 vecf32 类型存储
CREATE (:User {id: $id, emb: vecf32($embedding)})
-- 创建向量索引
CREATE VECTOR INDEX FOR (n:User) ON (n.emb)
OPTIONS {dimension: 64, similarityFunction: 'cosine'}
-- KNN 查询(向量同样需要包装为 vecf32)
CALL db.idx.vector.queryNodes('User', 'emb', 10, vecf32($vec))
YIELD node, score RETURN node.name, score
-- 注意:vecf32 类型不支持 Cypher reduce() 迭代,无法暴力扫描
Memgraph 3.0(原生 USearch 向量引擎):
cypher
-- 创建向量索引(3.0 原生语法,无需实验特性标志)
CREATE VECTOR INDEX user_emb_idx ON :User(emb)
WITH CONFIG {"dimension": 64, "capacity": 101000, "metric": "cos"}
-- KNN 查询(通过 vector_search 模块)
CALL vector_search.search('user_emb_idx', 10, $vec) YIELD node, distance
RETURN node.name, distance
-- 向量 + 属性过滤(注意:需要 WITH 子句,Memgraph 不支持 YIELD 后直接 WHERE)
CALL vector_search.search('user_emb_idx', 50, $vec) YIELD node, distance
WITH node, distance WHERE node.country = $c
RETURN node.name, distance LIMIT 10
4.4 计时方法
python
import time, statistics
def bench(fn, warmup=10, iters=100) -> dict:
# 预热,排除 JIT/缓存首次开销
for _ in range(warmup):
fn()
# 正式计时
latencies = []
for _ in range(iters):
t0 = time.perf_counter()
fn()
latencies.append((time.perf_counter() - t0) * 1000) # ms
latencies.sort()
return {
"median": statistics.median(latencies),
"p95": latencies[int(0.95 * iters)],
"p99": latencies[int(0.99 * iters)],
"qps": 1000 / statistics.median(latencies),
}
- 有索引查询:10 次 warmup + 100 次计时
- 无索引暴力扫描:3 次 warmup + 3 次计时(每次 ~22 秒,代价极高)
五、安装踩坑记录
5.1 Memgraph 向量搜索 Crash(v2.22.0 Bug)
最初安装的是 Memgraph 2.22.0,向量搜索是"实验特性",需要启动标志:
bash
# 启动时加 flag
memgraph --experimental-enabled=vector-search
结果:无论数据目录是否为空、是否禁用持久化,Memgraph 启动时必定 crash:
[info] Experimental feature vector-search is enabled.
You are running Memgraph v2.22.0
Vector index spec cannot be empty.
Aborted (core dumped)
通过 strings /usr/lib/memgraph/memgraph | grep "Vector index" 找到源码位置,发现:
Vector index spec must have a 'dimension' field.
Vector index spec cannot be empty. ← 就是这行
Vector index spec must have a 'property' field.
Vector index spec must have a 'label' field.
Vector index spec must have a 'capacity' field.
这是 v2.22.0 的已知 bug:当 vector-search 特性开启时,初始化代码期望找到预配置的向量索引规格,找不到就直接 ABRT,即便是全新空实例也如此。该 bug 的修复版本为 v3.0.0(向量搜索从实验特性升级为内置功能)。
5.2 Memgraph 3.0.0 配置文件 Crash
升级到 v3.0.0 后,直接通过 systemd 启动时仍然 crash。排查后发现:
旧配置文件中有一行:
--query-callable-mappings-path=/etc/memgraph/apoc_compatibility_mappings.json
这个 APOC 兼容性映射文件是 v2.22.0 时代的配置,在 v3.0.0 中行为发生变化,直接导致启动时崩溃。此外,v3.0.0 的 --experimental-enabled 已移除 vector-search 选项(仅保留 text-search),向量搜索完全内置,不需要任何标志。
修复:从配置文件中删除该行,只保留核心配置:
--bolt-port=7688
--data-directory=/data/graph-db-benchmark/volumes/memgraph/data
--log-level=WARNING
--storage-properties-on-edges=true
--query-modules-directory=/usr/lib/memgraph/query_modules
5.3 FalkorDB 向量索引首次查询 "Invalid arguments"
FalkorDB 的 HNSW 索引是异步构建的,CREATE VECTOR INDEX 语句返回后索引并未就绪。对 10 万个 64 维向量,HNSW 构建需要约 30 秒。在索引就绪前发起查询会报:
redis.exceptions.ResponseError: Invalid arguments for procedure 'db.idx.vector.queryNodes'
修复:用探针查询轮询等待,直到索引就绪:
python
def wait_for_index():
probe_vec = [0.0] * 64; probe_vec[0] = 1.0
for _ in range(24): # 最多等 120 秒
time.sleep(5)
try:
graph.query(
"CALL db.idx.vector.queryNodes('User','emb',1,vecf32($v)) YIELD node",
{"v": probe_vec}
)
return # 成功则退出等待
except:
pass # 继续等待
六、测试结果
6.1 数据加载性能
| 数据库 | 10万节点+64维向量 | 50万边 | 总加载时间 | 向量索引构建 |
|---|---|---|---|---|
| Neo4j 5.26 | ~17s | ~19s | 37.0s | 6.28s |
| FalkorDB 4.16 | ~18s | ~20s | 38.5s | 31.8s |
| Memgraph 3.0 | ~3s | ~4s | 7.0s | 33.94s |
分析:
- Memgraph 加载速度碾压其他两者,快 5 倍以上,得益于纯内存操作和高效的批量写入
- Neo4j 向量索引构建极快(6.28s),原因是 Neo4j 使用增量式 HNSW 构建,随数据写入同步建索引
- FalkorDB 和 Memgraph 索引构建都需要 ~32-34 秒,属于数据加载完成后的批量 HNSW 构建
6.2 无索引基线(暴力扫描)
全表扫描计算余弦相似度,使用 Cypher reduce() 函数:
cypher
-- Neo4j / Memgraph 暴力扫描(FalkorDB 的 vecf32 类型不支持元素迭代)
MATCH (u:User)
WITH u, reduce(dot=0.0, i IN range(0,63) | dot + u.emb[i] * $vec[i]) AS score
ORDER BY score DESC LIMIT 10
RETURN u.name, score
| 数据库 | 中位延迟 | p99 |
|---|---|---|
| Neo4j 5.26 | 21,564 ms(21.5 秒!) | 21,605 ms |
| Memgraph 3.0 | 22,315 ms(22.3 秒!) | 22,352 ms |
| FalkorDB 4.16 | N/A(vecf32 不可迭代) | --- |
结论:没有向量索引,100ms 以内的语义搜索根本不可能实现。向量索引的必要性不言而喻。
6.3 核心结果:向量 KNN 索引搜索(k=10)
这是最重要的测试项,也是语义搜索的核心场景。
| 数据库 | 中位延迟 | p95 | p99 | QPS |
|---|---|---|---|---|
| FalkorDB 4.16 | 0.298 ms | 0.4 ms | 0.518 ms | 3,353 |
| Memgraph 3.0 | 0.822 ms | 1.0 ms | 1.136 ms | 1,217 |
| Neo4j 5.26 | 2.672 ms | 6.0 ms | 7.932 ms | 374 |
关键发现:
- FalkorDB 中位延迟 0.298ms,已接近本地内存访问延迟级别,体现了 Redis 内核的极致优化
- Memgraph 0.822ms,介于 FalkorDB 和 Neo4j 之间,是不错的中间选择
- Neo4j p99 高达 7.932ms,是中位值的 3 倍,尾延迟抖动明显,不适合对延迟 SLA 敏感的场景
- 索引加速比:Neo4j 8,070×,Memgraph 27,148×(向量索引对这两个数据库的提升幅度都是万倍级)
6.4 向量 KNN + 属性过滤(country 过滤)
真实业务中往往需要"找语义相近 AND 满足条件的节点",例如"在中国的相似用户":
cypher
-- 先取 top50 近邻,再过滤属性(post-filter 模式)
CALL db.index.vector.queryNodes('user_emb_idx', 50, $vec) YIELD node, score
WHERE node.country = $country
RETURN node.name, score LIMIT 10
| 数据库 | 中位延迟 | p99 | QPS |
|---|---|---|---|
| FalkorDB 4.16 | 0.368 ms | 0.550 ms | 2,714 |
| Memgraph 3.0 | 0.921 ms | 1.065 ms | 1,086 |
| Neo4j 5.26 | 3.802 ms | 9.419 ms | 263 |
FalkorDB 在过滤场景下依然大幅领先,且 p99 非常稳定(0.55ms vs Neo4j 的 9.4ms)。
6.5 向量 KNN + 图扩展(最具图数据库特色的查询)
找到语义最相似的 5 个用户,再扩展他们的 FOLLOWS 关系(1 跳图遍历):
cypher
-- FalkorDB
CALL db.idx.vector.queryNodes('User', 'emb', 5, vecf32($vec)) YIELD node, score
MATCH (node)-[:FOLLOWS]->(nbr:User)
RETURN nbr.name, score ORDER BY score DESC LIMIT 20
-- Memgraph
CALL vector_search.search('user_emb_idx', 5, $vec) YIELD node, distance
WITH node, distance
MATCH (node)-[:FOLLOWS]->(nbr:User)
RETURN nbr.name, distance ORDER BY distance LIMIT 20
| 数据库 | 中位延迟 | p99 | QPS |
|---|---|---|---|
| FalkorDB 4.16 | 0.427 ms | 0.633 ms | 2,343 |
| Memgraph 3.0 | 0.994 ms | 1.182 ms | 1,006 |
| Neo4j 5.26 | 2.203 ms | 6.703 ms | 454 |
这是最能体现图数据库优势的查询,三者都能在毫秒内完成。FalkorDB 以 0.43ms 的中位延迟排名第一。
6.6 文本搜索对比
| 数据库 | CONTAINS 扫描(无索引) | 全文索引 |
|---|---|---|
| FalkorDB 4.16 | 0.273 ms | ✗ 不支持 |
| Memgraph 3.0 | 0.596 ms | ✗ 不支持 |
| Neo4j 5.26 | 1.445 ms | 1.449 ms |
文本搜索方面,FalkorDB 的 CONTAINS 速度最快(0.273ms),但仅支持精确子串匹配。Neo4j 的全文索引支持 Lucene 语法(模糊、分词、短语匹配),功能丰富得多。Memgraph 的文本搜索功能也在实验特性中,生产可用性存疑。
七、综合对比
7.1 延迟总览
向量 KNN(索引)中位延迟:
FalkorDB ████ 0.298ms ██████████████████████████████ QPS 3353
Memgraph █████████ 0.822ms ████████████ QPS 1217
Neo4j ████████████████████████████ 2.672ms ████ QPS 374
向量 KNN+过滤 中位延迟:
FalkorDB █████ 0.368ms
Memgraph ██████████ 0.921ms
Neo4j ████████████████████████████████████ 3.802ms
向量 KNN+图扩展 中位延迟:
FalkorDB ██████ 0.427ms
Memgraph ████████████ 0.994ms
Neo4j ████████████████████████ 2.203ms
7.2 各维度最优选手
| 维度 | 最优 | 次优 | 说明 |
|---|---|---|---|
| 向量 KNN 延迟 | FalkorDB 0.298ms | Memgraph 0.822ms | FalkorDB 快 Neo4j 9×,快 Memgraph 2.8× |
| 向量+过滤延迟 | FalkorDB 0.368ms | Memgraph 0.921ms | FalkorDB 快 Neo4j 10× |
| 向量+图扩展延迟 | FalkorDB 0.427ms | Memgraph 0.994ms | FalkorDB 快 Neo4j 5× |
| 文本搜索速度 | FalkorDB 0.273ms | Memgraph 0.596ms | 简单子串匹配场景 |
| 全文索引 | Neo4j 1.449ms | --- | 仅 Neo4j 支持 Lucene 语法 |
| 数据加载速度 | Memgraph 7s | Neo4j 37s | Memgraph 快 5× |
| 向量索引构建速度 | Neo4j 6.28s | FalkorDB 31.8s | Neo4j 快 5× |
| 延迟稳定性(p99/中位比) | FalkorDB 1.7× | Memgraph 1.4× | Neo4j p99 是中位的 3× |
| 功能完整性 | Neo4j | Memgraph | Neo4j 有全文索引、GDS 图算法、APOC |
| 运维简单度 | Neo4j | Memgraph | FalkorDB 依赖 Redis/Valkey,多一个组件 |
八、各数据库深度分析
8.1 FalkorDB:极低延迟的图+向量引擎
FalkorDB 将图数据库构建在 Redis/Valkey 之上,继承了 Redis 极致的内存访问速度。它的 vecf32 类型是专为向量搜索设计的原生类型,HNSW 索引实现经过深度优化。
优势:
- 极低延迟:0.3ms 的 KNN,3353 QPS,在语义搜索场景中遥遥领先
- 延迟稳定:p99 仅为中位值的 1.7 倍,尾延迟控制极好
- 文本搜索也快:即便是无索引的 CONTAINS 扫描也是三者中最快的
劣势:
vecf32类型不能被 Cypherreduce()等函数迭代,无法做暴力扫描(反而说明其向量存储高度特化)- 不支持全文索引
- 依赖 Redis/Valkey 作为底层存储,增加了运维复杂度(需要同时管理两个进程)
- 向量索引构建需 ~30s,数据导入后不能立即查询
适用场景:对延迟极度敏感的在线推荐、实时语义搜索、RAG 检索层;对文本搜索需求简单的场景。
8.2 Memgraph 3.0:潜力巨大的内存图数据库
Memgraph 使用 USearch 作为向量索引引擎,是一个高性能的近似最近邻搜索库。3.0 版本将向量搜索从实验特性升级为内置功能,标志着其向量检索能力的成熟。
优势:
- 数据加载极快:7 秒加载 10 万节点,比 Neo4j/FalkorDB 快 5 倍,非常适合需要频繁重建图的场景
- 延迟稳定:p99/中位比为 1.4,延迟最稳定(Neo4j 是 3×,FalkorDB 是 1.7×)
- 纯内存图:在内存充足的情况下,图遍历性能极佳
- 支持流式处理:Kafka/Pulsar 实时数据接入
劣势:
- 0.822ms 的 KNN 延迟介于 FalkorDB 和 Neo4j 之间,没有绝对优势
- 不支持全文索引(text-search 仍为实验特性)
- 向量索引构建也需要 ~34s
- 版本升级陷阱:v2.22.0 向量搜索有 crash bug,v3.0.0 的 apoc 配置文件格式发生变化,升级需谨慎
适用场景:需要快速重建图的场景(如每日全量更新);对延迟稳定性要求高但绝对延迟要求不极致的场景;实时流式图更新 + 语义搜索组合。
8.3 Neo4j 5.26:功能最全但延迟最高
Neo4j 是图数据库的行业标准,积累了十多年的生态。向量搜索从 5.11 版本开始支持,到 5.26 已相当成熟。
优势:
- 功能最完整:向量索引 + 全文索引 + GDS 图算法库 + APOC 工具集,是唯一同时支持所有搜索模式的
- 向量索引构建最快(6.28s):增量式 HNSW,随数据写入同步建索引,不需要等待批量构建
- 生态最成熟:Neo4j 的 Cypher 语法已成为图查询事实标准,驱动和工具生态丰富
- Lucene 全文索引:支持分词、模糊匹配、短语查询,是三者中唯一的生产级文本搜索
劣势:
- 延迟最高:2.672ms 的 KNN 延迟是 FalkorDB 的 9 倍
- 尾延迟抖动大:p99 达 7.932ms,是中位值的 3 倍,高并发场景下长尾问题明显
- QPS 仅 374,在高并发向量搜索场景下性能瓶颈明显
适用场景:功能优先于性能的场景;需要全文索引 + 向量索引混合搜索;需要 GDS 图算法(PageRank、社区发现等);团队熟悉 Cypher/Neo4j 生态。
九、工程实践建议
9.1 向量维度的选择
本次测试使用 64 维向量。实际生产中常见的嵌入维度:
all-MiniLM-L6-v2:384 维text-embedding-3-small(OpenAI):1536 维bge-m3:1024 维
维度越高,每次索引查询的计算量越大,延迟会相应增加。但 HNSW 的查询复杂度是 O(log N),维度增加对延迟的影响相对可控(通常 64→384 维约增加 2-3× 延迟)。
9.2 向量索引 Post-filter vs Pre-filter
本次测试的"向量+属性过滤"采用 Post-filter(先找 top-50,再过滤):
cypher
-- Post-filter:先找足够多的近邻,再过滤
CALL db.index.vector.queryNodes('idx', 50, $vec) YIELD node, score
WHERE node.country = 'China' -- 可能过滤掉大量结果
RETURN node.name LIMIT 10
这种方式的问题:如果过滤条件很严格(如 country='Andorra',人口极少),top-50 可能都被过滤掉,返回空结果。
生产中更健壮的方式是 Pre-filter(先过滤属性,再在子集上做 KNN),但需要数据库额外支持,Neo4j 的 GDS 和 FalkorDB 的部分版本支持此模式。
9.3 索引参数调优
HNSW 的两个关键参数:
- efConstruction(构建时质量参数):越大,索引质量越好,构建越慢
- M(每个节点的邻居数):越大,召回率越高,内存占用越大
本次测试使用默认参数。生产环境建议:
cypher
-- FalkorDB 调整 HNSW 参数
CREATE VECTOR INDEX FOR (n:User) ON (n.emb)
OPTIONS {dimension: 64, similarityFunction: 'cosine', M: 32, efConstruction: 400}
参数调大可提高召回率,但会增加索引构建时间和内存占用。
9.4 驱动选型
python
# Neo4j
from neo4j import GraphDatabase
driver = GraphDatabase.driver("bolt://localhost:7687", auth=("neo4j","password"))
# FalkorDB(通过 falkordb Python 客户端)
import falkordb
client = falkordb.FalkorDB(host="localhost", port=6379)
graph = client.select_graph("my_graph")
# Memgraph(使用 mgclient,mgclient-python 包名)
import mgclient
conn = mgclient.connect(host="127.0.0.1", port=7688)
conn.autocommit = True
十、结论
| 问题 | 答案 |
|---|---|
| 谁的向量检索延迟最低? | FalkorDB(0.3ms,秒杀级别) |
| 谁的 QPS 最高? | FalkorDB(3353 QPS) |
| 谁的延迟最稳定? | Memgraph(p99/中位比最低) |
| 谁加载数据最快? | Memgraph(7s vs 37s) |
| 谁功能最完整? | Neo4j(全文索引+向量+图算法) |
| RAG 应用选谁? | FalkorDB (延迟最低)或 Neo4j(功能最全) |
| 实时流式图场景选谁? | Memgraph |
10.1 推荐选择策略
延迟 < 1ms 是硬需求 → FalkorDB
需要全文索引(Lucene)+ 向量混合搜索 → Neo4j
数据需要频繁全量重建(批处理) → Memgraph
实时流式图更新场景 → Memgraph
团队技术栈偏向 Redis → FalkorDB
成熟生态/社区支持 → Neo4j
10.2 重要注意事项
-
Memgraph 版本 :如果使用 Memgraph,务必使用 3.0.0 以上版本,v2.22.0 的向量搜索有致命 crash bug,且配置文件格式与 3.x 不兼容
-
FalkorDB 向量类型 :FalkorDB 的向量属性必须使用
vecf32()函数包装存储,与普通浮点列表是不同类型,写入时需注意 -
向量索引异步构建 :FalkorDB 和 Memgraph 的向量索引都是异步构建的,
CREATE VECTOR INDEX返回后需要等待 30 秒左右才能查询,建议用探针查询检测就绪状态 -
Neo4j 社区版限制 :Neo4j Community Edition 不包含 Graph Data Science(GDS)插件,
gds.similarity.cosine()等函数不可用;向量索引是社区版内置功能,无需插件
附录:测试脚本核心代码
完整测试代码开源于:/data/graph-db-benchmark/benchmark_semantic.py
数据生成核心逻辑:
python
import numpy as np
def generate_embeddings(n: int, dim: int, seed: int = 42) -> np.ndarray:
"""生成 n 个 dim 维 L2 归一化向量"""
rng = np.random.default_rng(seed)
vecs = rng.standard_normal((n, dim)).astype(np.float32)
norms = np.linalg.norm(vecs, axis=1, keepdims=True)
return vecs / np.maximum(norms, 1e-9)
USER_EMBEDDINGS = generate_embeddings(100_000, 64) # 10万个节点向量
QUERY_VECTORS = generate_embeddings(500, 64, seed=99) # 500个查询向量
基准计时框架:
python
import time, statistics
def bench(fn, warmup: int = 10, iters: int = 100) -> dict:
for _ in range(warmup):
fn()
latencies = sorted(
(time.perf_counter() - (t := time.perf_counter()) or time.perf_counter() - t) * 1000
for _ in range(iters)
for t in [time.perf_counter()]
if not fn()
)
# 简化版:
latencies = []
for _ in range(iters):
t0 = time.perf_counter()
fn()
latencies.append((time.perf_counter() - t0) * 1000)
latencies.sort()
return {
"median": statistics.median(latencies),
"p95": latencies[int(0.95 * iters)],
"p99": latencies[int(0.99 * iters)],
"qps": 1000 / statistics.median(latencies),
}
测试环境:Ubuntu 24.04 LTS,单机,Neo4j 5.26.0 + FalkorDB 4.16.9 + Memgraph 3.0.0,10万节点 50万边,64维向量,顺序隔离测试。
参考资料: