cd /news/developer-tools/latticedb-vs-sqlite-i-ran-the-graph-… · home topics developer-tools article
[ARTICLE · art-115890] src=dev.to ↗ pub= topic=developer-tools verified=true sentiment=· neutral

LatticeDB vs SQLite: I Ran the Graph Traversal Benchmarks. The Gap Is Real but the Fine Print Matters

A developer benchmarked LatticeDB, an embedded property-graph database written in Zig, against SQLite for graph traversal and found the performance gap is real but concentrated in deep queries. The developer reports that LatticeDB is up to 2,819x faster at depth-50 traversals, while shallow queries show little difference, and notes the project's transparent benchmark methodology.

read7 min views3 publishedAug 30, 2026

Last week I needed to give my AI agent a memory that connects facts instead of just listing them. Not "the user likes Postgres," but "the user likes Postgres, worked with him at two companies, and every incident review he runs mentions connection pools." That is a graph. My first instinct was the one I always have: just use SQLite. And my second instinct, after two days of recursive CTEs, was to check what the HN front page was trying to tell me.

That same week, a Show HN called LatticeDB landed: an embedded, single-file property-graph database written in Zig, positioned as "like SQLite but for graph databases," with native HNSW vector search and BM25 full-text search in the same query layer. The marketing number going around is up to 2,819x faster graph traversal than SQLite. Numbers like that are usually a sign to keep scrolling. This time I did not. I read the benchmark methodology, then rebuilt the SQLite side myself and ran it on my own server.

What I found is more useful than either the hype or the dismissal: the gap is real, but it lives in one specific place. If your queries stay shallow, you will not see it. If they go deep, it is not a gap, it is a cliff.

One file, no server, embedded in your process, ACID with a WAL. That is the SQLite part. The difference is what the file is organized for: SQLite arranges rows into tables, LatticeDB arranges nodes into a graph, and puts three indexes over the same node properties.

Everything is queryable in one statement. From the README, this is the pitch in a single query: find chunks similar to an embedding, walk to their document, walk to the author.

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

The same job in Postgres today means pgvector for embeddings, tsvector for text, and recursive CTEs for the joins, then gluing three result sets in application code. In one embedded file with no server, that combination is genuinely new. MIT license, Python and TypeScript and Go bindings, about 300 GitHub stars when I checked, so this is an early-stage project and I treated it that way.

Full disclosure: I have read the source and the benchmark harness carefully, but I have not yet shipped anything on LatticeDB. The SQLite numbers below are mine, run on my hardware. The LatticeDB numbers are quoted from its published benchmark and its own head-to-head comparison doc.

The LatticeDB vs SQLite comparison is the only one in their docs measured in the same harness on the same machine, over a social-network graph with a power-law degree distribution, and they publish the command to reproduce it. That honesty is why I took the rest seriously.

The headline table, 100K nodes and 500K edges, adjacency cache warm:

Depth-limited traversal on a smaller 10K-node graph is where the eye-popping numbers live: 390x at depth 10, 713x at 15, 1,848x at 25, and 2,819x at depth 50, where SQLite needs 1.4 seconds and LatticeDB needs 500 microseconds. The docs themselves tell you how to read this: as "how much does depth cost you," not "LatticeDB is 3,000 times faster."

Elsewhere in the README, only the SQLite rows are head to head; the Neo4j and Kuzu numbers are third-party figures on hardware the authors do not control. Same caveat applies to the vector search table, where LatticeDB's 0.83 milliseconds for 10-nearest-neighbor over 1M vectors at 100 percent recall@10 competes with server databases like Weaviate and Qdrant that also pay network overhead, and beats sqlite-vec's brute-force 17 milliseconds by about 20x. Those are cross-benchmark comparisons, and the project says so. That kind of labeling is rare and it is the main reason I bothered rerunning anything at all.

The claims about LatticeDB are only as good as the SQLite side of the comparison, so I built my own version of it: a 100,000-node directed graph with 500,000 edges and a power-law in-degree distribution, the shape real social and citation graphs take. In-memory SQLite, one adjacency table, indexes on both columns, and the traversal written the way SQLite documentation actually recommends: a recursive CTE with UNION deduplication.

First attempt, I picked a random root node. It had 1 follower. Every traversal came back in microseconds, and for a moment the benchmarks looked like nonsense. Then I picked the most connected node, with 11,460 in-edges, and the real story appeared. Both runs are below, because the difference between them is the whole lesson.

From a random, low-degree node, everything is fast:

From the max-degree hub, the CTE cost explodes with depth:

My point lookups were never the problem: 2.2 microseconds for a primary-key hit, right in line with the roughly 0.2 microseconds LatticeDB reports for in-memory SQLite, and their docs admit the two engines are near-identical there. My server CPU is not an Apple M1, so do not compare my numbers to theirs row by row. Read the shape instead, because the shape is what transfers. At every depth, the recursive CTE costs explode as the frontier widens, each recursion level re-plans, and the UNION dedup compounds. That matches LatticeDB's published gap curve almost exactly, and it confirms the core mechanism behind their numbers: at depth, it is not that SQLite is slow, it is that per-level overhead is multiplied by frontier size, and frontier size in a power-law graph grows brutally.

Two honest caveats about my own test. The CTE ran per-level UNION deduplication; SQLite's CTE machinery is generic, while LatticeDB's BFS keeps a bitset of visited nodes and a warm adjacency cache, an apples-to-oranges specialization. And a hand-tuned application-level BFS in Python, batching the frontier with WHERE src IN (...) per level, would narrow the gap. It would not close it, because you would be re-implementing in application code what LatticeDB puts inside the engine next to the index. But I did not run that variant, so treat the 3.79 seconds as one honest measurement, not a ceiling or a floor.

After reading their docs and running my own numbers, here is the decision matrix I would actually use.

The honest one-liner from their docs deserves repeating: SQLite is better for the general case, LatticeDB is better for the specific shape where relationships, semantics, and text all matter to the same query.

This is why I went down this rabbit hole. My agent infra keeps per-user memory in SQLite today: a facts table, timestamps, full-text search via FTS5. It works, until the retrieval question becomes relational. "What do I know about this person connected to this project where the last interaction mentioned this library?" is three joins and a vector search away, and every hop costs a CTE recursion in a graph that keeps growing.

The changefeed idea is the sleeper feature here. If graph mutations come out as an ordered, replayable stream, then an embedding pipeline can react to new nodes without polling, and an audit log falls out for free since the stream shares the WAL path with the writes. My agent already writes an append-only audit trail, and getting that from the storage layer instead of maintaining it in application code is the kind of simplification I did not know I was shopping for.

But it is version 0.9.6 with a few hundred stars. I am not moving production memories this weekend. I am keeping an eye on the repo, and my plan is to prototype my agent memory on it in a side branch and see if the Cypher shape actually fits my queries. The 0.13 microsecond node lookup, the 0.83 millisecond vector search at 1M vectors, and that depth curve add up to something worth prototyping. None of it adds up to betting a product on a v0 database written in a language I cannot debug.

I write about databases, backend engineering, and AI infrastructure every week. Subscribe, it is free.

Have you hit the recursive CTE wall in SQLite, or are you running a graph database for agent memory already? What did you pick, and what did it cost you? I am genuinely torn between prototyping on LatticeDB and just living with FTS5 plus a hand-rolled adjacency cache, and I would like to hear from anyone who made either choice.

If you take one thing from this piece, make it this checklist:

── more in #developer-tools 4 stories · sorted by recency
── more on @latticedb 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/latticedb-vs-sqlite-…] indexed:0 read:7min 2026-08-30 ·