展示 HN:LatticeDB – 类似于 SQLite 但用于图数据库

Hacker News Top 产品

摘要

LatticeDB 是一款嵌入式属性图数据库,它将向量和全文索引集成到单一文件格式中,支持在一个查询层内进行图遍历、相似性搜索和BM25搜索,适用于本地应用。

我们工作中越来越多地使用图数据库,但发现本地操作起来很麻烦,于是决定尝试构建更好的解决方案。
查看原文
查看缓存全文

缓存时间: 2026/08/25 19:57

jeffhajewski/latticedb

来源:https://github.com/jeffhajewski/latticedb

LatticeDB

具备原生向量和全文索引能力的嵌入式属性图数据库。

LatticeDB 是一个单文件本地数据库,专为连接数据、语义数据和文本数据设计。它允许你在同一个引擎和查询层中遍历关系、执行向量相似度搜索以及进行 BM25 全文搜索。该数据库专为单机上的高关系性工作负载而设计,支持零配置操作和嵌入式单写者模型。

LatticeDB 是一个嵌入式、单文件的图数据库,允许本地应用通过关系、语义和文本对同一数据进行查询,并能从同一文件中获取持久化的图和应用程序事件。诸如图 RAG、智能体记忆和本地知识工具等工作负载正是基于这些基础功能构建的,而非引擎本身的定义。

  • 单文件。 你的整个数据库就是一个可移植的单文件。无需服务器,无需配置。
  • 单一查询层。 图遍历、HNSW 向量相似度搜索和 BM25 全文检索 —— 都在同一种查询语言中完成。
  • 单一事件日志。 持久化的命名流和内置的图变更流,与图写入共享相同的事务/WAL 路径。
  • 本地优先。 专为一台机器上的一个拥有进程设计,基于 WAL 提供持久性保证。
  • 高性能。 节点查找延迟 0.13 微秒。在 100 万向量数据集上进行向量搜索,延迟 0.83 毫秒,召回率 100%。

``cypher – 查找与查询向量相似的块,遍历到其所属文档,再到作者 MATCH (chunk:Chunk)-[:PART_OF]->(doc:Document)-[:AUTHORED_BY]->(author:Person) WHERE chunk.embedding <=> $query_vector < 0.3 AND doc.content @@ “neural networks” RETURN doc.title, chunk.text, author.name ORDER BY chunk.embedding <=> $query_vector LIMIT 10


## 安装

**CLI**

``bash
curl -fsSL https://raw.githubusercontent.com/jeffhajewski/latticedb/main/dist/install.sh | bash

Python

``bash pip install latticedb


发布的 wheel 包预计会在支持的平台上捆绑 `liblattice`。源码安装也可以在 wheel 构建期间通过设置 `LATTICE_BUNDLE_LIB_DIR=/path/to/lib` 来捆绑预置的本地库。

**TypeScript / Node.js**

``bash
npm install @hajewski/latticedb

发布的 npm 包预计会在支持的平台上捆绑 liblattice。源码检出可以通过 LATTICE_BUNDLE_LIB_DIR=/path/to/lib npm run bundle:native 将本地库放入包中。

Go

关于当前的 cgo 工作流程,请参见 bindings/go/README.md。默认消费路径使用已安装的 pkg-config 元数据;仓库内开发可以使用 -tags repolocal 针对 zig-out/lib

此外,在 examples/go 中还有一个可运行的图/向量/文本检索示例。

最近的绑定接口清理将嵌入式辅助工具移至专用模块和子包。关于首选导入和当前兼容性别名,请参见 docs/client_api_migration.md

从这里开始

  • 入门指南 为 CLI、Python、TypeScript 和 Go 指明了最短路径。
  • CLI 快速入门 是仓库中最小的复制粘贴示例。
  • 示例概览 涵盖了更大型的图/向量/文本检索演示。

示例

一个完整的示例:创建一个包含文档和作者的小型知识图谱,存储向量嵌入,索引文本,然后跨所有三种搜索模式进行查询。

Python

``python from latticedb import Database from latticedb.embedding import hash_embed

with Database(“knowledge.db”, create=True, enable_vectors=True, vector_dimensions=128) as db:

# --- 构建图 ---
with db.write() as txn:
    # 创建作者节点
    alice = txn.create_node(labels=["Person"], properties={"name": "Alice", "field": "ML"})
    bob = txn.create_node(labels=["Person"], properties={"name": "Bob", "field": "Systems"})
    txn.create_edge(alice.id, bob.id, "COLLABORATES_WITH")

    # 创建文档及其块节点
    for title, text, author in [
        ("Attention Is All You Need", "The transformer architecture uses self-attention...", alice),
        ("Scaling Laws for LLMs", "We find that model performance scales predictably...", alice),
        ("Log-Structured Merge Trees", "LSM trees optimize write-heavy workloads...", bob),
    ]:
        doc = txn.create_node(labels=["Document"], properties={"title": title})
        chunk = txn.create_node(labels=["Chunk"], properties={"text": text})

        # 存储向量嵌入并索引文本
        txn.set_vector(chunk.id, "embedding", hash_embed(text, dimensions=128))
        txn.fts_index(chunk.id, text)

        txn.create_edge(chunk.id, doc.id, "PART_OF")
        txn.create_edge(doc.id, author.id, "AUTHORED_BY")

    txn.commit()

# --- 查询:向量搜索 + 文本匹配 + 图遍历 ---
results = db.query("""
    MATCH (chunk:Chunk)-[:PART_OF]->(doc:Document)-[:AUTHORED_BY]->(author:Person)
    WHERE chunk.embedding <=> $query < 0.5
    RETURN doc.title, chunk.text, author.name
    ORDER BY chunk.embedding <=> $query
    LIMIT 5
""", parameters={"query": hash_embed("transformer attention mechanism", dimensions=128)})

for row in results:
    print(f"{row['doc.title']} by {row['author.name']}")

# --- 全文搜索 ---
for r in db.fts_search("self-attention transformer"):
    print(f"Node {r.node_id}: score={r.score:.4f}")

# --- 聚合查询 ---
stats = db.query("""
    MATCH (doc:Document)-[:AUTHORED_BY]->(p:Person)
    RETURN p.name, count(doc) AS papers
    ORDER BY papers DESC
""")
for row in stats:
    print(f"{row['p.name']}: {row['papers']} papers")

### TypeScript

``typescript
import { Database } from "@hajewski/latticedb";
import { hashEmbed } from "@hajewski/latticedb/embedding";

const db = new Database("knowledge.db", {
  create: true,
  enableVectors: true,
  vectorDimensions: 128,
});
await db.open();

// 构建一个图
await db.write(async (txn) => {
  const alice = await txn.createNode({
    labels: ["Person"],
    properties: { name: "Alice", field: "ML" },
  });
  const doc = await txn.createNode({
    labels: ["Document"],
    properties: { title: "Attention Is All You Need" },
  });
  const chunk = await txn.createNode({
    labels: ["Chunk"],
    properties: { text: "The transformer architecture uses self-attention..." },
  });

  await txn.setVector(chunk.id, "embedding", hashEmbed("transformer self-attention", 128));
  await txn.ftsIndex(chunk.id, "The transformer architecture uses self-attention...");

  await txn.createEdge(chunk.id, doc.id, "PART_OF");
  await txn.createEdge(doc.id, alice.id, "AUTHORED_BY");
});

// 跨向量搜索 + 图遍历进行查询
const results = await db.query(
  `MATCH (chunk:Chunk)-[:PART_OF]->(doc:Document)-[:AUTHORED_BY]->(author:Person)
   WHERE chunk.embedding <=> $query < 0.5
   RETURN doc.title, chunk.text, author.name
   ORDER BY chunk.embedding <=> $query
   LIMIT 5`,
  { query: hashEmbed("attention mechanism", 128) }
);

for (const row of results.rows) {
  console.log(`${row["doc.title"]} by ${row["author.name"]}`);
}

await db.close();

Go

``go db, err := latticedb.Open(“knowledge.db”, latticedb.OpenOptions{ Create: true, EnableVectors: true, VectorDimensions: 128, }) if err != nil { log.Fatal(err) } defer db.Close()

err = db.Update(func(tx *latticedb.Tx) error { node, err := tx.CreateNode(latticedb.CreateNodeOptions{ Labels: []string{“Chunk”}, Properties: map[string]latticedb.Value{“text”: “The transformer architecture uses self-attention…”}, }) if err != nil { return err } if err := tx.SetVector(node.ID, “embedding”, []float32{1, 0, 0, 0}); err != nil { return err } return tx.FTSIndex(node.ID, “The transformer architecture uses self-attention…”) }) if err != nil { log.Fatal(err) }


## 性能

基准测试在 Apple M1 上进行,单线程,使用自动扩缩缓冲池。运行 `zig build benchmark` 可复现。
对于之前暴露出二次追加行为的重复词项全文索引工作负载,运行 `zig build fts-benchmark`。

### 核心操作

| 操作 | 延迟 | 吞吐量 | 目标 | 状态 |
|-----------|---------|------------|--------|--------|
| 节点查找 | 0.13 μs | 7.9M 操作/秒 | < 1 μs | 通过 |
| 节点创建 | 0.65 μs | 1.5M 操作/秒 | — | — |
| 边遍历 | 9 μs | 111K 操作/秒 | — | — |
| 全文检索(100个文档) | 19 μs | 53K 操作/秒 | — | — |
| 10-NN 向量搜索(1M向量) | 0.83 ms | 1.2K 操作/秒 | < 10 ms @ 1M | 通过 |

### 规模化向量搜索 (HNSW)

128维余弦向量,M=16,ef_construction=200,ef_search=64,k=10。运行 `zig build vector-benchmark` 可复现。

| 规模 | 平均延迟 | P99延迟 | 召回率@10 | 内存占用 |
|-------|-------------|-------------|-----------|--------|
| 1,000 | 65 μs | 70 μs | 100% | 1 MB |
| 10,000 | 174 μs | 695 μs | 99% | 10 MB |
| 100,000 | 438 μs | 1.2 ms | 99% | 101 MB |
| 1,000,000 | 832 μs | 1.8 ms | 100% | 1,040 MB |

搜索延迟呈亚线性(O(log N))增长,召回率@10达到99-100%。使用启发式邻居选择(HNSW 论文算法4)以实现多样化的图连接,连接页打包实现约4.5倍的内存缩减,以及预归一化点积以实现快速余弦距离计算。

**ef_search 敏感度 (1M向量)**

| ef_search | 平均延迟 | 召回率@10 |
|-----------|-------------|-----------|
| 16 | 506 μs | 57% |
| 32 | 1.9 ms | 79% |
| 64 | 990 μs | 100% |
| 128 | 3.2 ms | 100% |
| 256 | 11.6 ms | 100% |

### 竞争分析

#### 点查找

| 系统 | 延迟 | 类型 | 来源 |
|--------|---------|------|--------|
| **LatticeDB** | **0.13 μs** | 嵌入式 | `zig build benchmark` |
| RocksDB(内存中) | 0.14 μs | 嵌入式 | RocksDB 维基 (https://github.com/facebook/rocksdb/wiki/RocksDB-In-Memory-Workload-Performance-Benchmarks) |
| SQLite(内存中) | ~0.2 μs | 嵌入式 | Turso 博客 (https://turso.tech/blog/microsecond-level-sql-query-latency-with-libsql-local-replicas-5e4ae19b628b) |
| SQLite (WAL, 磁盘) | 3 μs (p90) | 嵌入式 | marending.dev (https://marending.dev/notes/sqlite-benchmarks/) |
| Neo4j | 28 ms (p99) | 服务端 | Memgraph 对比 (https://memgraph.com/blog/memgraph-vs-neo4j-performance-benchmark-comparison) |

LatticeDB 的 B+树实现了亚微秒级缓存查找,性能与内存中的 RocksDB 相当,比磁盘上的 SQLite 快 23 倍。

#### 向量搜索

| 系统 | 延迟 (10-NN) | 规模 | 类型 | 来源 |
|--------|-----------------|-------|------|--------|
| **LatticeDB** | **0.83 ms 平均,100% 召回率** | 1M | 嵌入式 | `zig build vector-benchmark` |
| FAISS HNSW (单线程) | 0.5–3 ms | 1M | 库 | FAISS 维基 (https://github.com/facebookresearch/faiss/wiki/Indexing-1M-vectors) |
| Weaviate | 1.4 ms 平均,3.1 ms P99 | 1M | 服务端 | Weaviate 基准测试 (https://docs.weaviate.io/weaviate/benchmarks/ann) |
| Qdrant | ~1–2 ms | 1M | 服务端 | Qdrant 基准测试 (https://qdrant.tech/benchmarks/) |
| Milvus + SQ8 | 2.2 ms P99 | 1M | 服务端 | VectorDBBench (https://zilliz.com/vdbbench-leaderboard) |
| pgvector HNSW | ~5 ms @ 99% 召回率 | 1M | 扩展 | Jonathan Katz (https://jkatz05.com/post/postgres/pgvector-performance-150x-speedup/) |
| LanceDB | 3–5 ms | 1M | 嵌入式 | LanceDB 博客 (https://medium.com/etoai/benchmarking-lancedb-92b01032874a) |
| Chroma | 4–5 ms 平均 | 1M | 嵌入式 | Chroma 文档 (https://docs.trychroma.com/production/administration/performance) |
| Pinecone P2 | ~15 ms (含网络) | 1M | 云服务 | Pinecone 博客 (https://www.pinecone.io/blog/dedicated-read-nodes/) |
| sqlite-vec (暴力搜索) | 17 ms | 1M | 扩展 | Alex Garcia (https://alexgarcia.xyz/blog/2024/sqlite-vec-stable-release/index.html) |

LatticeDB 在 1M 规模下实现平均 0.83 毫秒,召回率@10达 100% —— 比 FAISS 单线程 HNSW 更快,并与 Weaviate 和 Qdrant 等基于服务端的系统具有竞争力(这些系统在实际中会增加网络开销)。

#### 图遍历

| 系统 | 2跳遍历 (100K节点) | 类型 | 来源 |
|--------|-------------------|------|--------|
| **LatticeDB** | **39 μs** | 嵌入式 | `zig build sqlite-benchmark` |
| SQLite (递归 CTE) | 548 μs | 嵌入式 | `zig build sqlite-benchmark` |
| Kuzu | 19 ms | 嵌入式 | The Data Quarry (https://thedataquarry.com/blog/embedded-db-2/) |
| Neo4j | 10 ms (1M节点) | 服务端 | Neo4j 博客 (https://neo4j.com/news/how-much-faster-is-a-graph-database-really/) |

**LatticeDB vs SQLite** —— 社交网络图(遵循幂律度分布),邻接缓存预热:

**小规模 (10K节点, 50K边)**

| 工作负载 | LatticeDB | SQLite | 加速比 |
|----------|-----------|--------|--------:|
| 1跳遍历 | 560 ns | 13.0 μs | **23x** |
| 2跳遍历 | 3.0 μs | 37.5 μs | **13x** |
| 3跳遍历 | 19.1 μs | 178.5 μs | **9x** |
| 可变路径 (1..5) | 82.4 μs | 4.3 ms | **52x** |

**中等规模 (100K节点, 500K边)**

| 工作负载 | LatticeDB | SQLite | 加速比 |
|----------|-----------|--------|--------:|
| 1跳遍历 | 8.0 μs | 290.0 μs | **36x** |
| 2跳遍历 | 38.7 μs | 548.3 μs | **14x** |
| 3跳遍历 | 197.3 μs | 1.2 ms | **6x** |
| 可变路径 (1..5) | 134.4 μs | 10.1 ms | **75x** |

**深度限制遍历 (10K节点, 50K边)**

| 深度 | LatticeDB | SQLite | 加速比 |
|------:|----------:|-------:|--------:|
| 10    | 311 μs    | 121 ms | **390x** |
| 15    | 380 μs    | 271 ms | **713x** |
| 25    | 318 μs    | 587 ms | **1,848x** |
| 50    | 500 μs    | 1.4 s  | **2,819x** |

LatticeDB 使用广度优先搜索(BFS),结合邻接缓存和位图访问记录。SQLite 使用带 `UNION` 去重的递归 CTE。两者计算出的可达节点集合相同(约 8K 节点)。随着深度增加,SQLite 的 CTE 开销随每个递归层级增长,差距进一步扩大。运行 `zig build graph-benchmark -- --quick` 可复现。

#### 全文检索 (BM25)

| 系统 | 搜索延迟 | 类型 | 来源 |
|--------|----------------|------|--------|
| **LatticeDB** | **19 μs** | 嵌入式 | `zig build benchmark` |
| SQLite FTS5 | < 6 ms | 嵌入式 | SQLite Cloud (https://blog.sqlite.ai/real-time-full-text-site-search-with-sqlite-fts5-extension) |
| Elasticsearch | 1–10 ms | 服务端 | 各种来源 |
| Tantivy | 10–100 μs | 库 | 各种来源 |

LatticeDB 的倒排索引结合 BM25 评分比 SQLite FTS5 快约 300 倍,性能可与 Tantivy(一个专用的 Rust 搜索库)相媲美。

## 功能特性

**图功能**
- 带标签和任意属性的节点和边
- 用于限定范围的节点和边属性的持久化显式相等索引
- 多跳遍历、可变长度路径 (`*1..3`)
- 支持提交/回滚和崩溃恢复的 ACID 事务
- MERGE、WITH、UNWIND、聚合函数 (`count`, `sum`, `avg`, `min`, `max`, `collect`)

**向量搜索**
- 可配置 M、ef 参数的 HNSW 近似最近邻搜索
- 内置哈希嵌入或用于 Ollama/OpenAI 的 HTTP 客户端
- 支持批量向量节点插入,实现快速数据摄取

**全文检索**
- 支持分词和词干提取的 BM25 评分倒排索引
- 支持可配置的莱文斯坦距离的模糊搜索

**Cypher 查询语言**
- MATCH、WHERE、RETURN、CREATE、DELETE、SET、REMOVE
- ORDER BY、LIMIT、SKIP、DETACH DELETE
- 向量距离运算符:`<=>`
- 全文检索运算符:`@@`
- 参数:`$name`

**运维**
- 单文件存储,带预写日志用于崩溃恢复
- 持久化的命名流,支持显式消费者偏移量、手动清理和图变更流
- 在线空闲空间复用,加上 `lattice compact` 用于安全的物理尾部回收
- 零配置 —— 打开一个文件即可开始工作
- 嵌入式单写者模型,适用于本地应用
- 干净的 C API;Python、TypeScript 和 Go 绑定对其进行了封装

## 用例

- **连接的本地数据** —— 笔记、文档、目录、引文图和实体图
- **图加检索** —— 关系遍历、语义搜索和词法搜索

相似文章

Show HN: HelixDB – 基于对象存储的图数据库

Hacker News Top

HelixDB 是一个用 Rust 构建的图-向量数据库,专为知识图谱和 AI 记忆设计,提供统一平台支持图、向量、键值、文档和关系型数据模型,并配有便于本地和云端部署的工具。

Fluree DB(GitHub 仓库)

TLDR AI

Fluree DB 是一个开源的时间图数据库,具有类似 Git 的分支、集成的向量/文本/地理搜索、细粒度的访问控制,并支持 SPARQL、JSON-LD 和 Open Cypher。它针对 AI 代理记忆进行了优化,在十亿级图上实现了高性能。

Slater – 专为读密集型图设计的低内存图数据库

Hacker News Top

Slater 是一款低内存图数据库,专为读密集型工作负载设计。它使用固定的缓存预算从磁盘提供大型图服务,仅需几百 MB 的 RAM 即可查询数亿节点和数十亿边,同时兼容标准 Bolt 协议并支持实时写入。