Lane

Graphs

Interactive and analytical queries on a social network graph.

Illustration for DBBench, an open benchmark of database engines

Results

Tasks and results.

The tasks come from the Linked Data Benchmark Council's Social Network Benchmark. The interactive half reads a graph at several depths: a point lookup, one hop, two hops, and a three-hop read with a property filter. It also inserts, updates and deletes.

The analytical half asks questions that touch the whole graph, such as the degree distribution and the triangle count. A triangle count returns one small number, so it is a strong answer check: if two engines disagree on it, one of them is wrong.

Each engine is asked in the language it is normally asked in. A query that exceeds its time budget is published as censored, with its budget, and is never dropped.

Graph interactive queries: point lookup, one hop, two hops, and a three-hop read with a property filter, with insert, update, and delete beside them. Warm medians, with the ninety-ninth percentile on the point lookup.

Graph OLTP LDBC-SNB Interactive (SF1, SF10)
EngineModeSizepoint p50 ms ↓point p99 ms ↓1-hop p50 ms ↓2-hop p50 ms ↓3-hop filtered p50 ms ↓insert p50 ms ↓update p50 ms ↓delete p50 ms ↓ingest vertices+edges/s ↑ingest total s ↓peak memory GiB ↓disk GiB ↓
ArcadeDBembeddedSF1 (10k people, 181k friendships)0.480.910.450.715.570.490.200.4987k2.182.790.013
ArcadeDBserverSF1 (10k people, 181k friendships)1.231.841.181.476.611.260.861.0725k7.543.360.012
ArangoDBserverSF1 (10k people, 181k friendships)0.720.961.002.2817.00.800.680.8698k1.940.500.048
DuckPGQembeddedSF1 (10k people, 181k friendships)0.250.290.932.243.3010.59.0610.5187k1.020.190.041
FalkorDBserverSF1 (10k people, 181k friendships)0.130.280.220.340.610.160.120.7641k4.700.180
LadybugDBembeddedSF1 (10k people, 181k friendships)re-runre-runre-runre-runre-runre-runre-runre-runre-runre-runre-runre-run
MemgraphserverSF1 (10k people, 181k friendships)0.150.350.150.211.170.150.140.1472k2.660.460.024
MongoDBserverSF1 (10k people, 181k friendships)0.0900.280.270.9078.90.230.0860.32131k1.450.250.022
Neo4jserverSF1 (10k people, 181k friendships)6.1112.75.664.666.297.145.896.0637k5.214.740.015
SurrealDBembeddedSF1 (10k people, 181k friendships)0.0440.0510.6915.2190.00.180.0830.1514k13.80.800.10
SurrealDBserverSF1 (10k people, 181k friendships)0.110.610.170.5728.8re-runre-runre-run20k9.510.310.047
ArcadeDBembeddedSF10 (66k people, 1.9M friendships)0.511.050.621.5523.40.520.200.4687k238.190.13
ArcadeDBserverSF10 (66k people, 1.9M friendships)1.131.671.302.3624.91.320.831.1528k71.97.990.14
ArangoDBserverSF10 (66k people, 1.9M friendships)0.901.121.066.0092.40.910.740.9391k21.90.780.41
DuckPGQembeddedSF10 (66k people, 1.9M friendships)0.260.331.334.276.9610.69.0210.8275k7.280.310.16
FalkorDBserverSF10 (66k people, 1.9M friendships)0.130.420.240.661.510.170.100.2124k85.30.370
LadybugDBembeddedSF10 (66k people, 1.9M friendships)re-runre-runre-runre-runre-runre-runre-runre-runre-runre-runre-runre-run
MemgraphserverSF10 (66k people, 1.9M friendships)0.150.400.150.303.710.150.140.1471k28.10.660.24
MongoDBserverSF10 (66k people, 1.9M friendships)0.0890.330.401.71345.20.220.0860.32161k12.51.100.19
Neo4jserverSF10 (66k people, 1.9M friendships)7.1812.37.076.2313.27.546.236.4544k45.413.10.66
SurrealDBembeddedSF10 (66k people, 1.9M friendships)0.0470.0581.3860.71,2170.200.0890.1811k176.25.671.09
SurrealDBserverSF10 (66k people, 1.9M friendships)0.170.600.401.24210.5re-runre-runre-run18k111.32.140.26

↑ higher is better, ↓ lower is better; the best value in each column, within one size, is bold

Measured rows: benchmarks/experiments/results/runs_paper_oct.csv

Exact builds measured
  • arcadedb 26.10.1-dev · 417314c18 (in-process)
  • arcadedb 26.10.1-dev · 417314c18 arcadedb-c25:417314c18
  • arangodb 3.12.11 arangodb@563cb2c07af0…
  • duckdb 1.5.4 + duckpgq f386a6c
  • falkordb 4.20.6 falkordb/falkordb@0a9fe4d1ee0b…
  • memgraph 3.13.1 memgraph/memgraph@4710bee1ab5b…
  • mongodb 8.2.12 mongo@41afd6e1183f…
  • neo4j 2026.08.1 neo4j@e702d6b535d9…
  • surrealdb 2.3.10
  • surrealdb 3.2.4 surrealdb/surrealdb@6a5002363ff5…
  • Disk is what the workload left on disk, in GiB: the engine's writable layer plus its volumes after the cell, minus the same engine's empty footprint.
  • LadybugDB's row is marked `re-run`: it sized its thread count and buffer pool from the whole machine rather than from the cores and memory its cell was given, while every other engine here that sizes itself from the machine was fitted to its cell. It is being re-measured with both fitted.
  • SurrealDB (server)'s write cells are marked `re-run`: the server ran at its default, a sync to disk at every commit, although it has a setting that does not wait, and every other engine on this table that has such a setting ran at it. Our harness missed that setting, and the next measurement runs its writes at it. Its read cells commit nothing and stay.
  • In this measurement every person's age loaded as zero, through a date-parsing error in our data loader, so the 3-hop filtered read (age above 30) matched no one on any engine: its times are three hops plus a check on every end node. The next measurement parses the ages correctly.
  • Each friendship is one edge, pointing from the person with the smaller id to the person with the larger. The engines can follow an edge from either end, but the 1-hop, 2-hop, and 3-hop reads follow it only the way it points, so 1-hop counts only the friends with a larger id than the start person, and 2-hop and 3-hop reach only people along a chain of rising ids. The next measurement asks the same questions in both directions of a friendship.
  • Each read on this table is timed twice over the same start persons, and the table prints the first pass, so its read columns include each engine's warm-up. On ArcadeDB (embedded), ArcadeDB (server), and Neo4j, which run on a JVM, that warm-up includes the JVM compiling the query path. The next measurement gives every engine an untimed warm-up on other start persons first, prints the warm median, and adds the first query of a session as the cold column.
  • Durability is matched at the relaxed end: on every engine that has the setting, a commit returns without waiting for the disk and the log is flushed by the engine's own background policy, which is ArcadeDB's engine default and a documented production mode for each of the others. Each row records what it ran as `durability`, and the setting was read out of the engine rather than assumed on every engine except ArcadeDB (server) and SurrealDB (server). ArcadeDB (server) is given the setting when the server starts, and the row records it, because the server offers no way to read it back. The exceptions on this table are DuckPGQ and Neo4j, which have no setting to relax and wait for the disk at every commit; their write and transaction cells are paying for that. SurrealDB (server)'s rows record that no setting was found, and its server log shows what it ran: its default in both runs, a sync to disk at every commit (the log reads "Sync mode: every transaction commit" in every run), although it has a setting that does not wait; the next measurement runs both.
  • Every engine traverses the same persons-and-KNOWS projection, with edges stored in both directions.
  • ArcadeDB (embedded), ArcadeDB (server), and Neo4j run on a JVM, and for them this column is close to the heap they were given rather than the memory the work needed: Neo4j's 4.74 GiB here is about 119 per cent of its 4g heap. Every engine on a table gets the same memory envelope; how much of it a JVM takes is a property of the runtime, so read these cells against each other rather than against an engine that manages its own memory.
  • Every repetition, on every engine, runs each read against the same start persons, 500 at SF1 (10k people, 181k friendships) and 200 at SF10 (66k people, 1.9M friendships), plus 1,000 each of the insert, update, and delete; the p50 and p99 are over those. The start persons are picked at random with a fixed seed and with replacement, so one person can be picked more than once.
  • FalkorDB at SF1 (10k people, 181k friendships) and SF10 (66k people, 1.9M friendships): its disk cells are 0.0 because the server container did not grow over its empty footprint during the run: at the relaxed setting FalkorDB persists only by timed snapshots, with no append-only file, and none had been taken when disk was measured, so the graph was held in memory only; read them as a floor under 0.01 GiB, not as a size.
  • In the cells: `re-run` means the cell was measured before a fix to our harness and stays off the table until it is measured again; the note says what was wrong. A dash is an operation the engine cannot express, never a slow one.
  • Ingest paths: ArcadeDB embedded loads through the Java API (newVertex, newEdge) in 5,000-record transactions; served sends CREATE VERTEX and CREATE EDGE statements as sqlscript batches over HTTP; Neo4j and Memgraph UNWIND batches over bolt; FalkorDB the same UNWIND batches over the Redis protocol; LadybugDB COPY from CSV, its native bulk path; DuckPGQ registers each batch as an Arrow table and INSERT ... SELECTs from it into the Person and knows tables, then defines the property graph over them; ArangoDB import_bulk; MongoDB insert_many; SurrealDB inserts the persons through its Python SDK and the KNOWS edges as bulk relation inserts.

Still being measured at this version. This table appears here when its run finishes.

The rules every result follows are in the methodology. The settings for each engine are on the engines page. If one is wrong, tell us.

DBBench

The other lanes.

  • Tables: Documents and structured query language (SQL) analytics.
  • Vectors: Dense and sparse vector search, with recall beside every latency.
  • Time series: Monitoring-style queries over timestamped readings.
  • Cross-model: One operation across a vector, a graph edge, and a document.
  • Lifecycle: Opening, closing, deployment boundaries, and what waiting for the disk costs.