An early look at the DuckDB storage engine for MariaDB — columnar, vectorized analytics that live right next to your transactional tables.
The problem
MariaDB’s InnoDB is excellent at what it was built for: transactions. …
Continue reading \"DuckDB Storage Engine for MariaDB. When the Sea Lion Learns to Quack.\"
The post DuckDB Storage Engine for MariaDB. When the Sea Lion Learns to Quack. appeared first on MariaDB.org.
DuckDB Storage Engine for MariaDB. When the Sea Lion Learns to Quack. appeared first on MariaDB.org
An early look at the DuckDB storage engine for MariaDB — columnar, vectorized analytics that live right next to your transactional tables.
The problemMariaDB’s InnoDB is excellent at what it was built for: transactions. Row-by-row inserts, updates, point lookups, strong consistency. But the moment you ask it to scan tens of millions of rows for a multi-way join with a few aggregations, a row store has to work hard.
The usual answer is to stand up a separate analytical system, then build ETL pipelines to copy data into it. Now you maintain two systems and you live with the lag between them.
What if the analytical engine just lived inside the same server — same SQL, same client, same data directory?
What we builtThe DuckDB storage engine for MariaDB does exactly that. Create a table with ENGINE=DuckDB and its data is stored in DuckDB‘s native columnar format; analytical queries against it run through DuckDB’s vectorized, multi-core engine — in-process, inside the MariaDB server.
No separate cluster. No ETL. No new protocol. The familiar mariadb client, one SQL dialect, InnoDB and DuckDB tables side by side in the same database.
MariaDB already ships ColumnStore, a mature columnar engine for large-scale analytics — typically deployed as a scale-out, multi-node data warehouse. The DuckDB engine targets a different point on the spectrum: a single-node, in-process, zero-ops analytical engine that sits next to your InnoDB tables for lightweight embedded analytics and HTAP. The two are complementary, not competing — reach for ColumnStore when you need a distributed warehouse, and the DuckDB engine when you want in-process analytics with no extra moving parts.
A 30-second tasteCreate a DuckDB table just like any other:
CREATE TABLE sales (
id BIGINT PRIMARY KEY,
product VARCHAR(64),
amount DECIMAL(12,2),
sold_at TIMESTAMP
) ENGINE=DuckDB;Query it with ordinary SQL — the whole SELECT is pushed down to DuckDB:
SELECT product, SUM(amount) AS revenue
FROM sales
GROUP BY product
ORDER BY revenue DESC
LIMIT 10;And the part that makes it genuinely useful — cross-engine joins. A single query can combine a DuckDB analytical table with an InnoDB operational table. Here it is end to end — two tables in two different engines, joined in one statement:
-- analytical data in DuckDB, operational data in InnoDB
CREATE TABLE analytics.orders (id BIGINT PRIMARY KEY, product_id INT, amount DECIMAL(12,2)) ENGINE=DuckDB;
CREATE TABLE inventory.products (id INT PRIMARY KEY, name VARCHAR(64)) ENGINE=InnoDB;
INSERT INTO inventory.products VALUES (1, 'Wireless Keyboard'), (2, 'Mechanical Switch');
INSERT INTO analytics.orders VALUES (4217, 1, 1299.00), (4218, 2, 42.00);
-- one query, both engines
SELECT d.id, d.amount, i.name
FROM analytics.orders d -- ENGINE=DuckDB
JOIN inventory.products i -- ENGINE=InnoDB
ON d.product_id = i.id
WHERE d.amount > 1000;DuckDB handles the join, aggregation, and sorting; the InnoDB rows are produced on demand through the MariaDB optimizer. No copying, no ETL.
PerformanceWe benchmarked the engine on TPC-H SF10 (~11 GB, 86.6M rows, 60M in lineitem). Two things matter for an analytical engine: how fast you can load data, and how fast you can query it.
Hardware / environment: Intel Core i7-13700H (14 cores / 20 threads), 64 GB RAM, NVMe SSD. MariaDB 11.4.13 with embedded DuckDB v1.5.2, duckdb_memory_limit=8 GiB, threads=20. Warm runs, stable to ~±15%.
The full suite runs warm in ~4.3 seconds:
| Query | Time (s) | Query | Time (s) | Query | Time (s) |
|---|---|---|---|---|---|
| q01 | 0.253 | q09 | 0.337 | q17 | 0.116 |
| q02 | 0.076 | q10 | 0.253 | q18 | 0.318 |
| q03 | 0.134 | q11 | 0.049 | q19 | 0.207 |
| q04 | 0.135 | q12 | 0.152 | q20 | 0.166 |
| q05 | 0.142 | q13 | 0.606 | q21 | 0.486 |
| q06 | 0.070 | q14 | 0.128 | q22 | 0.113 |
| q07 | 0.133 | q15 | 0.101 | Total | 4.30 |
| q08 | 0.145 | q16 | 0.178 |
Each query also pays a small fixed cost (~40 ms) for client connect and pushdown setup — noticeable on the cheapest queries, negligible on the heavy analytical ones where DuckDB’s vectorized execution dominates.
Bulk loading| Path | Total load |
|---|---|
In-engine COPY (run_in_duckdb, 8 GB mem) | 33 s |
LOAD DATA LOCAL INFILE | ~400 s (6.7 min) |
Loading external CSVs through DuckDB’s native parallel reader in-process — via the run_in_duckdb() function — ingests all 86.6M rows in ~33 s. The standard LOAD DATA LOCAL INFILE path is ~12× slower because every row is routed through MariaDB’s SQL/handler layer, serializing what COPY does in parallel. For bulk loads, prefer in-engine COPY via run_in_duckdb and raise duckdb_memory_limit for large tables.
DuckDB’s speed rests on three pillars: columnar storage (read only the columns you need), vectorized execution (process data in cache-friendly batches), and parallelism (use every core).
Inside MariaDB, the engine plugs into two places:
SELECT (its WHERE, JOIN, GROUP BY, ORDER BY) is handed to DuckDB through MariaDB’s select_handler interface, so DuckDB’s optimizer plans the whole query.That’s the short version. A deeper architecture post is coming.
Why a pluginThe DuckDB engine ships as a MariaDB plugin — and that’s a deliberate, important choice. MariaDB’s pluggable architecture lets a component like this have independent maturity from the server itself.
This is exactly the model that lets MariaDB ship ambitious new capabilities safely: powerful when you want it, invisible when you don’t.
When to use itGood fit
Current limitations (it’s early days)
DECIMAL precision is capped at 38 digits (DuckDB’s limit).PRIMARY KEY and reject non-UTF8 charsets.GROUP_CONCAT(), DATE_FORMAT()) and fall back to MariaDB execution.GROUP BY than MariaDB’s default sql_mode allows.XA transactions; cross-engine external scans are currently single-threaded.This is just the opening move. Where we’re headed next:
The quickest path: MariaDB’s community CI publishes ready-built packages here. Pick the directory matching your platform and architecture — there’s one per OS/arch combination, so you have to choose the appropriate path. For Ubuntu 24.04 (amd64) that’s:
https://ci.mariadb.org/70621/amd64-ubuntu-2404-deb-autobake/Use that directory as a package repository (or just download the .deb files from it) to install without compiling. Swap in the matching folder for other targets — e.g. amd64-rhel-9-rpm-autobake/, aarch64-ubuntu-2404-deb-autobake/, and so on. Note that these packages are not signed and suitable only for testing purposes.
Alternatively, the engine ships in-tree under storage/duckdb/. Clone the branch matching your target MariaDB version and build:
git clone --recurse-submodules -b 11.4 https://github.com/MariaDB/server.git mariadb-server
cd mariadb-server
./storage/duckdb/build.sh -D # install build dependencies (root)
./storage/duckdb/build.sh # build + installBuild a package yourselfPrefer to roll your own distributable package? After installing the build dependencies (build.sh -D), build.sh -p produces an RPM or DEB for your platform. See the Build packages section of the engine README for details:
./storage/duckdb/build.sh -p # build RPM (Rocky/Fedora/Amazon Linux) or DEB (Debian/Ubuntu)Enable the engineThe DuckDB engine must be loaded at server startup — this step is obligatory. Drop a duckdb.cnf file into your server’s config-include directory:
/etc/mysql/mariadb.conf.d/duckdb.cnf[mysqld]
plugin-maturity=alpha
plugin-load-add=ha_duckdb.so
duckdb-memory-limit=8G # default is only 1G — worth raising before testingplugin-load-add loads the engine and plugin-maturity=alpha is required because the plugin is currently at alpha maturity. Note the default duckdb-memory-limit is just 1 GB — for any serious testing it’s worth tweaking upward first, as it’s the main bottleneck for large loads and queries. Restart the server and you’re ready to create ENGINE=DuckDB tables.
This is a preview — we’d love testers, bug reports, and feedback.
LicensingThe engine is licensed under the GPL v2 (the same license as MariaDB Server), while DuckDB itself is distributed under the permissive MIT License.
CreditsThis work draws on the DuckDB project, whose clean, embeddable, MIT-licensed engine makes integrations like this feasible, and on Alibaba’s AliSQL, whose December 2025 release integrating DuckDB into a MySQL-compatible server was a valuable reference and proof point.
More to come — including a deeper dive on the architecture and a fuller benchmark study.
| # | Наименование новости | Тональность | Информативность | Дата публикации |
|---|---|---|---|---|
| 1 | MariaDB + DuckDB: A New Playground for Analytics – A First Look at the New Storage Engine | 0 | 2.25 | 12-06-2026 |
| 2 | The DuckDB MySQL engine at 500 GB | 0 | 10.07 | 07-08-2026 |
| 3 | Running DuckDB as a MySQL 9.7 storage engine | 0 | 12.2 | 10-07-2026 |
| 4 | DuckDB Speed on MySQL with dbtrail, Without a New Storage Engine | 0 | 10.5 | 26-08-2026 |
| 5 | Replicating from InnoDB into a DuckDB storage engine | 0 | 5.51 | 13-08-2026 |
| 6 | Fast analytics with MariaDB ColumnStore | 0 | 5.63 | 17-01-2024 |
| 7 | MariaDB Contribution Statistics, January-June 2026 | 0 | 11.03 | 25-08-2026 |
| 8 | MariaDB Community Server Q3 2026 maintenance releases | 0 | 18.63 | 24-08-2026 |
| 9 | MariaDB R2DBC Connector 1.4.1 now available | 0 | 13.16 | 16-06-2026 |
| 10 | Simple tool to build MariaDB commits for performance-change analysis | 0 | 6.98 | 18-06-2026 |