Latest posts
Faster DuckDB Queries with Vortex
Oct 21, 2026 · by Connor Tsui · 4 min read
This is part one of a series about Vortex and DuckDB. We're starting with a short introduction, and later posts will go into more technical detail.
DuckDB 2.0
has just been released!
It brings a new storage format, asynchronous I/O, and improvements to query
execution.
If you use Vortex with DuckDB, you might be able to get even faster query
performance!
DuckDB supports
querying external files
(like CSV or Parquet), so we can keep our data in
a different format without first loading it into DuckDB's native storage.
Vortex is one such file format. Similar to Parquet,
it is an open columnar file format built for analytical workloads, but Vortex
can also work on data while it is still compressed, which mitigates some of the
significant performance costs of decompression.
Vortex is available as a DuckDB
core extension on
Linux and macOS, so you can read from and write to Vortex files directly inside
DuckDB.
How Vortex makes DuckDB faster
Compression can reduce how much data a query reads from disk or object storage,
but decompression still takes valuable CPU time. If we can perform an operation
before decompressing the data, we can avoid much of that work.
Vortex ships with a highly optimized reader alongside its file format, with code
for reading and computing over compressed data. An engine can delegate and "push
down" supported operations to this reader, so Vortex can serve DuckDB as well as
other database query engines.
DuckDB is responsible for plannig the SQL queries and handles joins and
aggregates, while the Vortex reader scans the requested columns and evaluates
supported filters. Vortex and DuckDB both work with batches of columnar values,
and Vortex can often zero-copy pass the data up to DuckDB.
For example, ClickBench runs 43
analytical queries over
hits, a single table of web analytics data. Query 20
counts rows whose URL contains google:SQL
SELECT COUNT(*)
FROM hits
WHERE "URL" LIKE '%google%';
Vortex can
search strings while they're still compressed,
and DuckDB counts the matching rows, so we can avoid the work of reconstructing
full URLs that the query never returns.
In our ClickBench results, DuckDB +
Vortex runs Q20 about 3x faster than both DuckDB + Parquet and DuckDB's
native storage. Across all 43 queries, DuckDB + Vortex is about 2x faster
than both by geometric mean.
Relative ClickBench query time log scale, lower is faster
Q0
Q1
Q2
Q3
Q4
Q5
Q6
Q7
Q8
Q9
Q10
Q11
Q12
Q13
Q14
Q15
Q16
Q17
Q18
Q19
Q20
Q21
Q22
Q23
Q24
Q25
Q26
Q27
Q28
Q29
Q30
Q31
Q32
Q33
Q34
Q35
Q36
Q37
Q38
Q39
Q40
Q41
Q42
Show all timingsHide timings
| Query | Vortex (ms) | Parquet (ms) | Native (ms) |
|---|---|---|---|
| Q0 | 2.5 | 12.9 | 16.0 |
| Q1 | 7.8 | 19.2 | 34.8 |
| Q2 | 8.4 | 27.1 | 28.0 |
| Q3 | 16.2 | 26.6 | 26.8 |
| Q4 | 57.8 | 66.4 | 83.7 |
| Q5 | 66.0 | 91.5 | 87.8 |
| Q6 | 2.8 | 25.3 | 30.3 |
| Q7 | 9.1 | 20.9 | 23.1 |
| Q8 | 69.1 | 79.0 | 98.2 |
| Q9 | 91.0 | 106.2 | 103.5 |
| Q10 | 27.7 | 45.6 | 63.2 |
| Q11 | 30.2 | 51.9 | 67.0 |
| Q12 | 71.8 | 99.5 | 93.3 |
| Q13 | 129.6 | 163.3 | 167.0 |
| Q14 | 77.4 | 109.9 | 99.2 |
| Q15 | 76.8 | 93.9 | 94.7 |
| Q16 | 143.2 | 181.1 | 192.2 |
| Q17 | 148.2 | 182.7 | 175.3 |
| Q18 | 262.6 | 310.4 | 323.3 |
| Q19 | 9.3 | 23.8 | 23.2 |
| Q20 | 65.6 | 198.4 | 210.5 |
| Q21 | 98.3 | 256.2 | 190.3 |
| Q22 | 178.0 | 331.6 | 201.3 |
| Q23 | 36.8 | 188.0 | 84.3 |
| Q24 | 14.6 | 36.0 | 41.2 |
| Q25 | 19.3 | 49.0 | 54.6 |
| Q26 | 11.5 | 37.2 | 41.8 |
| Q27 | 55.9 | 204.2 | 173.1 |
| Q28 | 455.6 | 851.2 | 735.1 |
| Q29 | 18.8 | 32.0 | 34.8 |
| Q30 | 62.1 | 111.9 | 114.8 |
| Q31 | 112.4 | 168.9 | 169.7 |
| Q32 | 381.7 | 405.1 | 496.2 |
| Q33 | 362.1 | 482.6 | 498.6 |
| Q34 | 365.5 | 486.7 | 506.9 |
| Q35 | 59.4 | 65.4 | 85.6 |
| Q36 | 12.5 | 37.5 | 39.4 |
| Q37 | 8.8 | 28.1 | 23.4 |
| Q38 | 9.5 | 31.0 | 27.6 |
| Q39 | 21.0 | 63.9 | 63.5 |
| Q40 | 7.7 | 18.8 | 22.4 |
| Q41 | 8.4 | 18.5 | 21.8 |
| Q42 | 7.5 | 18.8 | 21.9 |
2e41681 (October 21, 2026). The geomean baseline is the geometric mean of the three times for each query. Hover or tap a row for exact numbers.Every ClickBench query finishes in under a second with all three formats, so
these differences might not matter much on their own. But we also run
benchmarks at larger scales, where DuckDB with
Vortex is faster than DuckDB with Parquet on local NVMe.
Reading and writing Vortex from DuckDB
Suppose we already have our data in a Parquet file like
hits.parquet. After
loading the
Vortex extension, we
can use DuckDB to read that Parquet file and write hits.vortex, then run the
same query against the new file:SQL
INSTALL vortex;
LOAD vortex;
COPY (SELECT * FROM 'hits.parquet')
TO 'hits.vortex' (FORMAT vortex);
SELECT COUNT(*)
FROM 'hits.vortex'
WHERE "URL" LIKE '%google%';
On local storage, you should expect most analytical queries to run faster with
Vortex than with Parquet.1 If you want to compare the
formats, simply run the same
SELECT against hits.parquet (the only
difference is the file name in the FROM clause)!The DuckDB integration guide covers
other ways to read and write Vortex files, including files in object storage.
Why store the data in Vortex?
Faster queries are one reason to store data in Vortex. Another reason is that
your data can be accessible outside of DuckDB. By keeping it in
.vortex files
instead of DuckDB's native storage, we can use another engine with Vortex
support without having to export the dataset. Parquet is the usual choice for
this kind of data, but as the ClickBench results above show, DuckDB can query
Vortex faster than Parquet, so we don't have to trade speed for portability.For example, we can explore
hits.vortex in DuckDB but use
DataFusion to
query the same file from a service, so we don't need a separate copy of the data
for each engine or a pipeline to keep those copies in sync. Vortex also works
with Apache Arrow, the in-memory columnar format
that many data systems share, so the same files work in
PyArrow workflows as well as SQL
queries.Sharing files between engines works well for a single dataset, but many teams
have to manage large datasets as tables in an open table format such as
Apache Iceberg. Iceberg tables keeps track of
many data files, along with the table's schema and history. In practice, those
data files are almost always Parquet, which means Iceberg tables inherit
Parquet's limits: Parquet can be slower to query, and it is
hard to extend with new types or encodings.
We want Iceberg users to have a faster, more extensible option, so we've been
working on using Vortex files as the data files in Iceberg tables. That
technical work is complete, and we're now
seeking acceptance from the Iceberg community.
Put together, Vortex can give DuckDB faster queries on data that other engines
can still read. If you already use DuckDB, trying Vortex only takes
INSTALL vortex;, LOAD vortex;, and a COPY to write your data as a
.vortex file. Later posts in this series will go into more technical detail
about how DuckDB and Vortex work together.Footnotes
-
If they don't, we'd like to hear about it. Join the Vortex Slack or open an issue and tell us about your workload. ↩