Influxdb VS QuestDB VS ClickHouse

After the disaster that was InfluxDB’s later versions, I decided it was time to move on from InfluxDB 1.11. For my evaluation, I tested three databases using real-world data and the actual queries I rely on. The versions tested were:

InfluxDB: 1.11.8

QuestDB: 8.2.3

ClickHouse: 25.3.2

The test setup included two tables:

• price with over 7.7 billion rows (7,745,168,884)

• 1dayprice with around 13.5 million rows (13,580,249)

ClickHouse only outperforms in specific queries like full time-range selects—for example, fetching BTC data from 1dayprice. However, after converting and testing multiple queries, it consistently performed slower in most other cases. So, while ClickHouse is a powerful database, it’s not the best fit for these scenarios.

To clarify, I’m not testing import or ingestion speed—only SELECT query performance. All databases were pre-loaded with batch writes.

All DB use Compressions, For clickhouse column, for influxdb and questdb ZFS

Influxdb(ZFS)QuestDB(ZFS)ClickHouse
384G254G225G

Now let’s get into the testing. I won’t be using any benchmarking tools—just running the actual queries I use in production.

QuestDBInfluxdb
SELECT
coin,
last(price_btc) AS price_last_btc,
last(price) AS price_last,
last(volume) AS value24_native,
(last(volume) * last(price)) AS value24,
first(price) AS yprice,
last(mcap) AS mkcap,
last(supply) AS supply,
avg(price) AS price24_mean,
min(price) AS min,
max(price) AS max
FROM ‘price_all’
WHERE time >= dateadd(‘h’, -24, now())
GROUP BY coin
SELECT
last(price_btc) as btc,
last(price) as price,
last(volume) as volume,
first(price) as price24,
last(mcap) as mcap,
last(supply) as supply,
mean(price) as price24_mean,
min(price) as min_price,
max(price) as max_price
FROM price_all WHERE time > now() – 24h GROUP BY coin
89 milliseconds1160 milliseconds
QuestDBInfluxdb
SELECT
coin,
first(price) as ‘7day’,
first(volume) as ‘7day_vol_n’,
(
first(price) * first(volume)
) as ‘7day_vol’,
first(mcap) as ‘7day_mcap’,
max(price) as ‘7day_maxp’,
min(price) as ‘7day_minp’
FROM
‘price_all’
WHERE
time >= dateadd(‘h’, -180, now())
SELECT
first(price),
first(volume),
(
first(price) * first(supply)
),
first(mcap),
max(price),
min(price)
FROM
\ “price_all\”
WHERE time >= now() – 180h AND
time <= now() – 158h GROUP BY coin
307 milliseconds736 milliseconds
QuestDBInfluxdb
SELECT
coin,
first(price) as ‘1day’,
first(volume) as ‘1day_vol_n’,
(first(price) * first(volume)) as ‘1day_vol’,
first(mcap) as ‘1day_mcap’,
max(price) as ‘1day_maxp’,
min(price) as ‘1day_minp’
FROM ‘price_all’
WHERE time >= dateadd(‘h’, -24, now())
SELECT
time,
first(price),
first(volume),
(
first(price) * first(supply)
)
FROM
\ “price_all\”
WHERE time >= now() – 24h GROUP BY coin
53 milliseconds334 milliseconds
QuestDBInfluxdb
SELECT
day,
coin,
first(price) AS s_open,
min(price) AS s_min,
max(price) AS s_max,
last(price) AS s_close
FROM (
SELECT
date_trunc(‘day’, time) AS day,
coin,
price
FROM price_all
WHERE time > dateadd(‘d’, -2, now())
)
GROUP BY coin, day
SELECT
time,
first(price),
min(price),
max(price)
FROM
“price_all”
WHERE time > now() – 2d
GROUP BY coin,time(1d)
716 milliseconds1862 milliseconds
QuestDBInfluxdb
SELECT t1.coin, t1.avg_price AS ath_price, CAST(t1.time AS STRING) as ath_time
FROM “1dayprice” t1
JOIN (
SELECT coin, max(avg_price) AS max_avg_price
FROM “1dayprice”
GROUP BY coin
) t2
ON t1.coin = t2.coin AND t1.avg_price = t2.max_avg_price;
SELECT
time,max(avg_price)
FROM
“1dayprice”
GROUP BY coin
606 milliseconds21507 milliseconds
QuestDBInfluxdb
SELECT
coin,
first(avg_price) as ’30day’,
first(volume) as ’30day_vol’,
first(mcap) as ’30day_mcap’,
FROM “1dayprice”
WHERE time >= dateadd(‘d’, -31, now())
AND time <= dateadd(‘d’, -29, now())
GROUP BY coin
SELECT
time,
first(avg_price),
first(volume),
first(mcap)
FROM
\ “1dayprice\” WHERE time >= now() – 31d AND time < now() – 29d GROUP BY coin
12 milliseconds167 milliseconds
QuestDBInfluxdb
SELECT
coin,
first(avg_price) AS f_price_p,
CAST(first(time) AS STRING) AS f_price
FROM ‘1dayprice’
GROUP BY coin;
SELECT time,first(avg_price) FROM \”1dayprice\” GROUP BY coin
236 milliseconds21032 milliseconds

From this point something interesting is happening

QuestDBInfluxdb
SELECT
time,
last(price) AS price,
last(mcap) AS mcap,
last(volume) * last(price) AS market_volume,
last(price_btc) AS price_btc
FROM price_all
WHERE coin = ’90’ AND time > dateadd(‘d’, -1, now())
SAMPLE BY 10m
ALIGN TO CALENDAR
SELECT
last(price),
last(mcap),
(
last(volume) * last(price)
),
last(price_btc)
FROM
“price_all”
WHERE coin=’90’
AND time > now() – 1d group by time(10m) fill(none)
(First run 10556) After 102 milliseconds52 milliseconds

In this situation as long I go in past as more time questdb needs, seams after first request it cache response but as soon cache expire request difference is insane.

QuestDBInfluxdb
SELECT
max(high_price),
max(mcap),
max(volume),
max(price_btc)
FROM ‘1dayprice’
WHERE coin=’90’
GROUP BY time(7d)
FILL(none)
SELECT
max(high_price),
max(mcap),
max(volume),
max(price_btc)
FROM
“1dayprice” WHERE coin=’90’
group by time(7d) fill(none)
449 milliseconds278 milliseconds
QuestDBInfluxdb
SELECT
high_price,
mcap,
volume,
price_btc
FROM
“1dayprice”
WHERE
coin = ’90’
AND time > dateadd(‘d’, -365, now());
SELECT
high_price,
mcap,
volume,
price_btc
FROM
“1dayprice”
WHERE coin=’90’
AND time > now() – 365d
172 milliseconds168 milliseconds

Overall, QuestDB significantly outperforms InfluxDB v1 in most queries. However, there are issues with certain queries that use SAMPLE BY. Even on the second execution, performance drops noticeably as the cache expires quickly—making it a deal-breaker for our use case.

Another strange issue we encountered: when executing a large number of requests to retrieve the full dataset, QuestDB would freeze and become unresponsive. Since it’s Java-based, we tried tuning the JVM parameters, but the problem still persisted.

-Xmx34g
-Xms18g

t’s performing better now after some tuning, but it still doesn’t seem to handle concurrency as well as InfluxDB. Based on system load observations, QuestDB appears less stable under high parallel query loads—possibly due to memory not being freed efficiently or in time.

The query runs in a loop across all price points from the downsampled table. Under the same workload, InfluxDB handles it smoothly—CPU usage stays under 20% and it maintains responsiveness with just 15GB of memory. In contrast, QuestDB starts to struggle, showing signs of instability and inconsistent performance under load.

Table in QuestDB

CREATE TABLE price_all (
    time     TIMESTAMP,
    coin     SYMBOL CAPACITY 60000 CACHE INDEX,
    mcap     DOUBLE,
    price    DOUBLE,
    supply   DOUBLE,
    volume   DOUBLE
)
TIMESTAMP(time)
PARTITION BY DAY
DEDUP UPSERT KEYS(time, coin);

After running extensive tests across multiple scenarios, I decided to also give TimescaleDB a try. QuestDB definitely shows promise, but it’s clear that it’s not quite there yet for our production needs.

Leave a Reply

Your email address will not be published. Required fields are marked *