TimescaleDB vs QuestDB: 2026 Benchmark Results

Compare QuestDB and TimescaleDB with up-to-date performance benchmarks and architectural comparisons.

Nic Hourcard
Nic HourcardFounder / CEO
QuestDB is the open-source time-series database for demanding workloads—from trading floors to mission control. It delivers ultra-low latency, high ingestion throughput, and a multi-tier storage engine. Native support for Parquet and SQL keeps your data portable, AI-ready—no vendor lock-in.

TimescaleDB and QuestDB are both open-source time-series databases. TimescaleDB is a PostgreSQL extension that adds time-series storage and functions to a database many teams already run. QuestDB is a standalone engine built for ingestion throughput and query latency.

TimescaleDB is the right choice when you already run PostgreSQL and want time series inside it. You keep your drivers and tooling and get hypertables and continuous aggregates on top. QuestDB is for the workloads PostgreSQL was never built for. It ingests millions of rows a second and serves the latest state in milliseconds with years of history behind it, for the cases where a single lost record has consequences.

This article runs both through the same TSBS benchmark on the same machine, TimescaleDB 2.29.1 against QuestDB 9.3.3, publishes every number, and then looks at the architectural differences behind the results.

Info

These benchmarks predate QWP, the binary protocol we shipped in QuestDB 10.0 in August 2026. Ingestion here runs over InfluxDB Line Protocol, a text format the server parses row by row. QWP sends binary columns instead, up to 3.6x faster over a network, and it streams reads back as Arrow. Read the QuestDB figures below as a floor.

Introducing TimescaleDB & QuestDB

DB-Engines classifies them like this:

FeatureQuestDBTimescaleDB
Primary database modelTime Series DBMSTime Series DBMS
Implementation languageJava (zero-GC), C++, RustC
SQL?YesYes
APIsQWP, ILP, HTTP, PGWire, JDBCJDBC, ODBC, C lib

TimescaleDB is a time series database built atop PostgreSQL. It's provided under two licenses: Apache 2.0 and the proprietary Timescale License. It's an extension of PostgreSQL, rather than a standalone database. If you already run PostgreSQL, it is the familiar way in.

Want to see a QuestDB comparison vs. InfluxDB? Read the article

QuestDB is an open-source time series database licensed under Apache License 2.0. It is a standalone engine rather than an extension, written for ingestion throughput and query latency, and it stores historical data as Parquet so the data stays readable without QuestDB. Queries are standard SQL with time-series extensions.

Performance benchmarks

Ingestion first, then queries. We use the Time Series Benchmark Suite (TSBS) for both. TSBS was created by Timescale and has seen no real development upstream since 2023, so we maintain our own fork.

Ingestion benchmark results

Hardware:

  • AWS EC2 r8a.8xlarge instance with 32 vCPU and 256 GB RAM (AMD EPYC)

  • GP3 EBS storage configured for 20,000 IOPS and 1 GB/s throughput

And on the software side:

  • Ubuntu 22.04

  • TimescaleDB 2.29.1 on PostgreSQL 17.10, tuned to the hardware with timescaledb-tune (roughly 19 settings, including ~60 GB of shared buffers)

  • QuestDB 9.3.3 with the out-of-the-box configuration

Measured: TimescaleDB 2.29.1, benchmarked August 2026. QuestDB 9.3.3, on the same r8a.8xlarge instance. New benchmarks on QuestDB 10.0 are coming.

Core allocation: the TSBS client and the database run on the same instance with no CPU pinning.

Query statistic: each figure is the median latency within a run, averaged over 3 runs, with a warm query cache.

The benchmark compares ingestion speed between QuestDB and TimescaleDB using the TSBS cpu-only use case. Data is sent via 32 concurrent connections. We test four scale factors representing different cardinality levels. We start at 1,000 hosts. Smaller datasets finish in under a second on the faster engines, where setup cost dominates and the result stops measuring throughput.

Generating and loading the QuestDB side, changing --scale for each level:

$ ./tsbs_generate_data --use-case="cpu-only" --seed=123 --scale=4000 \
--timestamp-start="2016-01-01T00:00:00Z" \
--timestamp-end="2016-01-03T00:00:00Z" \
--log-interval="10s" --format="questdb" > /tmp/questdb_data
$ ./tsbs_load_questdb --file=/tmp/questdb_data --workers=32

TimescaleDB is loaded from the same seed with --format="timescaledb" and tsbs_load_timescaledb, also with 32 workers.

↑ Higher is better

8.6M

QuestDB peak rows/sec, at 100K hosts

1.3M

TimescaleDB peak rows/sec, at 1K hosts

Scale (hosts)QuestDB (rows/sec)TimescaleDB (rows/sec)QuestDB Advantage
1,0007,770,0001,310,6495.9x faster
4,0007,960,0001,242,8106.4x faster
100,0008,590,0001,075,1788.0x faster
1,000,0007,140,000904,4247.9x faster

QuestDB holds between 7.1 and 8.6 million rows per second from 1,000 hosts all the way to 1 million, which is the property that matters here: adding series does not cost QuestDB anything structural.

TimescaleDB peaks at 1.31 million rows per second for 1,000 hosts, then degrades as cardinality rises, dropping to 904K rows/sec at 1M hosts. It is also the only engine here tuned to the hardware: timescaledb-tune sets roughly 19 parameters during setup, while QuestDB runs its out-of-the-box configuration. The comparison below is therefore a tuned TimescaleDB against an untuned QuestDB.

The row-based write path is the reason, and the architecture section below goes into it.

Query benchmark results

As part of the standard TSBS benchmark, we test several types of popular time series queries: single-groupby (aggregating metrics for specific hosts over time ranges), double-groupby (aggregating across all hosts), cpu-max-all, the high-cpu threshold scans, lastpoint and top-N.

All query benchmarks run with a single worker against the 4,000-host dataset (69,120,000 rows), 1,000 queries per type:

$ ./tsbs_generate_queries --use-case="devops" --seed=123 --scale=4000 \
--timestamp-start="2016-01-01T00:00:00Z" \
--timestamp-end="2016-01-03T00:00:00Z" \
--queries=1000 --query-type="single-groupby-1-1-1" \
--format="questdb" > /tmp/questdb_query
$ ./tsbs_run_queries_questdb --file=/tmp/questdb_query --workers=1

TimescaleDB runs the same generated queries through tsbs_run_queries_timescaledb.

Single-Groupby Queries

These queries aggregate CPU metrics for random hosts over specified time ranges. Query names encode metrics, hosts and hours: 5-8-1 is five metrics, eight hosts, one hour. Every query on this page is described in the appendix.

↓ Lower is better

Query format: metrics-hosts-hours · median of 3 runs, warm cache

1.9 ms

QuestDB, single-groupby-5-1-12

4.0 ms

TimescaleDB, single-groupby-5-1-12

QueryQuestDB (median)TimescaleDB (median)QuestDB Advantage
single-groupby-1-1-10.41 ms0.54 ms1.3x faster
single-groupby-1-1-121.43 ms3.49 ms2.4x faster
single-groupby-1-8-10.91 ms2.39 ms2.6x faster
single-groupby-5-1-10.60 ms0.57 msTimescaleDB 1.1x
single-groupby-5-1-121.94 ms4.00 ms2.1x faster
single-groupby-5-8-11.17 ms2.56 ms2.2x faster

QuestDB leads five of the six single-groupby queries, by up to 2.6x once a query spans multiple hosts. TimescaleDB edges single-groupby-5-1-1, the shortest of the six, by 1.1x.

Double-Groupby Queries

These queries aggregate across ALL 4,000 hosts, grouped by host and 1-hour intervals - a common analytical workload.

↓ Lower is better

Aggregates across ALL 4,000 hosts, grouped by host and 1-hour intervals · median of 3 runs, warm cache

56 ms

QuestDB, double-groupby-all

1.2 s

TimescaleDB, double-groupby-all

QueryQuestDB (median)TimescaleDB (median)QuestDB Advantage
double-groupby-132.20 ms2,211 ms*68.7x faster
double-groupby-541.20 ms2,962 ms*71.9x faster
double-groupby-all56.10 ms1,199 ms21.4x faster

* These two are slower than double-groupby-all on the same run, which is counter-intuitive: aggregating one metric should not cost more than aggregating ten. We re-ran them on three separate instances and got 2,208.51, 2,212.48 and 2,210.94 ms for double-groupby-1, and 2,960.13, 2,960.51 and 2,966.66 ms for double-groupby-5. The behaviour is stable rather than a bad run, so we publish what we measured.

These scan every host for the whole window and aggregate by host and hour. QuestDB reads only the columns it needs and aggregates them across all cores; TimescaleDB works row by row through PostgreSQL's executor.

CPU-max-all Queries

These take the maximum of all ten CPU metrics across 1, 8 and 32 hosts. They are the queries where the gap widens fastest as more series come into scope.

↓ Lower is better

Maximum of all 10 CPU metrics, across 1, 8 and 32 hosts

5.4 ms

QuestDB, cpu-max-all-32-24

219 ms

TimescaleDB, cpu-max-all-32-24

QueryQuestDB (median)TimescaleDB (median)QuestDB Advantage
cpu-max-all-11.48 ms2.58 ms1.7x faster
cpu-max-all-85.40 ms17.19 ms3.2x faster
cpu-max-all-32-245.37 ms218.50 ms40.7x faster

TimescaleDB slows by 85x between one host and 32 while QuestDB slows by 3.6x, which is the clearest illustration on this page of what per-series overhead costs at query time.

Heavy Analytical Queries

These scan for hosts with CPU utilization above a threshold, either for one host or across all of them.

↓ Lower is better

Finding hosts with CPU utilization above threshold · median of 3 runs, warm cache

1.03 s

QuestDB, high-cpu-all

488 ms

TimescaleDB, high-cpu-all

QueryQuestDB (median)TimescaleDB (median)Best
high-cpu-13.31 ms2.99 msTimescaleDB 1.1x
high-cpu-all1,027.50 ms487.70 msTimescaleDB 2.1x

TimescaleDB wins both. On the full scan it is more than twice as fast, and it has improved sharply here: the same query took 1,082 ms on 2.23.1. Worth noting that TimescaleDB is tuned to the hardware on this page while QuestDB runs defaults.

NB: QuestDB 10.0 was measured in the same run. Over QWP, its new binary protocol, it answers high-cpu-all in 672.60 ms. The tables on this page stay on 9.3.3, the version the whole suite was run against; full 10.0 results will come with our next benchmark suite rather than a TSBS re-run.

Lastpoint and Top-N Queries

↓ Lower is better

Latest reading per host, and top-N aggregation

1.7 ms

QuestDB, lastpoint

20.5 ms

TimescaleDB, lastpoint

11 ms

TimescaleDB, groupby-orderby-limit

QueryQuestDB (median)TimescaleDB (median)Best
lastpoint1.70 ms20.50 ms12.1x faster
groupby-orderby-limit16.76 ms11.09 msTimescaleDB 1.5x

lastpoint retrieves the most recent reading for every host, one of the most common time-series operations there is. QuestDB's LATEST ON is built for exactly this pattern. groupby-orderby-limit tests top-N aggregation, where TimescaleDB is ahead.

NB: QuestDB 10.0 was measured in the same run. Over QWP, its new binary protocol, it answers groupby-orderby-limit in 11.75 ms. The tables on this page stay on 9.3.3, the version the whole suite was run against; full 10.0 results will come with our next benchmark suite rather than a TSBS re-run.

Architecture overview

Why the numbers come out this way.

TimescaleDB overview

TimescaleDB is a PostgreSQL extension. A table becomes a hypertable, which PostgreSQL partitions into chunks by time interval and, optionally, by a space key, so each chunk is a smaller table covering one time range.

An image from Timescale's docs demonstrating a hypertable.
From Timescale.com, Hypertable architecture

Storage is hybrid. Recent chunks stay in PostgreSQL's row format, so inserts go through the usual heap and B-tree path. Older chunks are converted to Hypercore, TimescaleDB's compressed columnar format, for analytical queries. Writes land in row format first and are converted later, where QuestDB writes columnar from the start.

Everything PostgreSQL offers is available: joins, secondary and partial indexes, extensions such as PostGIS. One constraint carries over: a unique index or primary key on a hypertable must include the time column, because PostgreSQL enforces uniqueness per chunk.

QuestDB architecture

QuestDB is an open-source time series database written in zero-GC Java, C++ and Rust. Data comes in over QWP or InfluxDB Line Protocol and queries are SQL. Storage has three tiers:

Tier 1 - Write-Ahead Log (WAL): Incoming writes land in a WAL for durability. The WAL buffers and sorts out-of-order data before committing to storage.

Tier 2 - Columnar partitions: Data is organized into time-partitioned columnar files. Each partition stores columns in separate files, so queries read only the columns they need and scan them in parallel with SIMD.

Tier 3 - Cold storage (Parquet): Older partitions can be converted to Parquet and moved to object storage (S3, Azure Blob, GCS). Queries span all tiers.

WHERE symbol in ('AAPL', 'NVDA')
LATEST ON timestamp PARTITION BY symbol
CREATE MATERIALIZED VIEW 'trades_OHLC'
min(price) AS low
timestamp IN '$today'
SELECT spread_bps(bids[1][1], asks[1][1])
FROM read_parquet('trades.parquet')
SAMPLE BY 15m

Unlike TimescaleDB's hypertable architecture built on PostgreSQL's row-based storage, QuestDB stores all time series in a single columnar table structure. This means adding new series (high cardinality) doesn't create additional storage overhead, the same columnar files simply contain more rows. This architectural difference explains why QuestDB maintains consistent performance as cardinality scales.

QuestDB stores history as Parquet, returns query results as Arrow over QWP, and since 10.0 writes Parquet that the Iceberg ecosystem reads directly, so the data is never locked to QuestDB. Tiering cold partitions to object storage is a QuestDB Enterprise feature. TimescaleDB's data lives in PostgreSQL's storage format. Both speak the PostgreSQL wire protocol, so PostgreSQL tooling works with either.

Filters are compiled with a JIT compiler and run in parallel over time-partitioned storage. New columns are added on ingest, and schema changes do not block writes. Clients connect over QWP, InfluxDB Line Protocol, PGWire or REST, with client libraries in the main languages, and the SQL carries time-series extensions.

TimescaleDB limitations

Both have limitations. TimescaleDB first.

Temporal JOINs and missing time-based extensions

Timescale does not support joining tables based on the nearest timestamp, also known as ASOF JOIN. These joins are everywhere in financial markets.

Some common time-series operations are awkward too. Retrieving the latest reading per symbol or per sensor means a lateral join. Compare:

-- QuestDB provides ASOF JOIN. Since table definition includes
-- the designated timestamp column, no time condition is needed
SELECT t.*, n.*
FROM trades ASOF JOIN news ON (symbol);

There is no SAMPLE BY equivalent for periodic intervals, though continuous aggregates cover the fixed-interval case by pre-aggregating into summary tables.

Schema change overhead

Schema changes go through PostgreSQL's ALTER TABLE, which on a large hypertable can mean long locks and rewrites, depending on the change.

Ingestion and cardinality

Insert speed depends on how many indexes, foreign keys and unique constraints the table carries, and Timescale's own tuning guide is largely about removing them. Ingestion also degrades as cardinality rises, because of the row-based write path and the indexing. The recommended mitigation, fewer indexes and constraints, runs counter to what PostgreSQL is usually chosen for.

PostgreSQL extension

Installing TimescaleDB requires PostgreSQL first, then the extension, then time-series-specific tuning. QuestDB is dependency-free and ready out of the box. Less of an issue on managed cloud platforms, but adds maintenance overhead when self-hosting.

QuestDB limitations

Now QuestDB.

Type of workload & use case

TimescaleDB, built on PostgreSQL's row-based architecture, supports OLTP operations (INSERT, UPSERT, UPDATE, DELETE, triggers). QuestDB is columnar and optimized for OLAP and time series workloads: high ingest throughput, fast queries, and time-based analytics common in financial services, IoT, and Ad-Tech.

QuestDB is not designed for OLTP and is less suited for log monitoring use cases.

Weaker PostgreSQL compatibility

QuestDB supports PGWire, enabling use with most PostgreSQL libraries. However, it only supports forward-only cursors, not scrollable cursors via DECLARE CURSOR and FETCH. Some drivers like psycopg2 rely on scrollable cursors and may not be fully compatible.

Ecosystem maturity

Timescale has the larger set of third-party integrations. QuestDB's ecosystem is growing but smaller.

Is QuestDB a TimescaleDB replacement?

Not if what you want is PostgreSQL. TimescaleDB adds time series to a database you already run and know how to operate. At moderate volumes that is a sensible choice, and in this benchmark, tuned to the hardware, it wins the two threshold scans and the top-N query.

QuestDB is the stronger choice where speed is mission critical and data cannot afford to be lost: market data and trades, telemetry from an energy plant, a rocket or a fleet of robots. Those are high-speed time series, and a row store built for transactions isn't the right shape for them. QuestDB's architecture is: columnar storage, ingestion in the millions of rows per second, the latest state in milliseconds, time-series joins, and one SQL engine from live data to years of history.

The benchmark shows the gap. On default settings QuestDB ingests six to eight times faster than a tuned TimescaleDB at every cardinality, and it is far ahead on everything that aggregates across hosts. TimescaleDB stays close on the narrowest single-host reads.

QuestDB speaks the PostgreSQL wire protocol, so the drivers and tools you use today connect to it; what you give up is the rest of PostgreSQL, which the page covers above. If you're building something new and the data will matter, starting on QuestDB saves a migration later.

To get started, try our live demo, download QuestDB, or explore QuestDB Enterprise for production deployments.

Info

For a single-workload comparison showing how QuestDB's dedicated SQL operators affect performance vs. standard SQL rewrites on TimescaleDB, see How we made WINDOW JOIN parallel and vectorized. This is not a standardized benchmark like TSBS.

Appendix: what each TSBS query does

Query names encode their parameters: single-groupby-5-8-1 is five metrics, eight hosts, one hour.

QueryWhat it measures
single-groupby-M-H-TMaximum of M CPU metrics for H random hosts, in 5-minute buckets over T hours. 1-1-1 is one metric, one host, one hour; the -12 variants cover twelve hours
double-groupby-MAverage of M CPU metrics for every host, grouped by host and by hour, over 24 hours. all is all ten metrics
cpu-max-all-HMaximum of all ten CPU metrics for H random hosts, per hour. -1 and -8 cover one hour; -32-24 covers 32 hosts over 24 hours
high-cpu-1Every reading where one metric is above a threshold, for one random host
high-cpu-allThe same threshold scan across every host, which is a full-table scan
lastpointThe most recent reading for every host
groupby-orderby-limitThe last five per-minute aggregates before a random point in time, ordered by time, a top-N query

Definitions follow the TSBS README. Every query is generated with seed 123 against the 4,000-host dataset, so each engine answers the same random hosts and time ranges.

More database comparisons

Subscribe to stay up to date with all things QuestDB.