来源:https://mariadb.org/mariadb-duckdb-a-new-playground-for-analytics-a-first-look-at-the-new-storage-engine/
MariaDB + DuckDB:数据分析的新游乐场——初探新存储引擎
MariaDB 刚刚宣布它学会了"嘎嘎叫":新的 DuckDB 存储引擎已加入 MariaDB 服务器的存储引擎大家庭。
这个想法非常有趣:像往常一样使用 MariaDB 服务器,但使用 ENGINE=DuckDB 创建一些表,并利用 DuckDB 的列式存储和向量化执行来处理分析查询。
换句话说,我们可以在 InnoDB 中保留事务性工作负载,同时在同一个 MariaDB 实例中使用 DuckDB 表进行分析。这不是一个新概念,但却是一个新的、强大的实现!
当然,我现在不会推荐在生产环境中使用它;它仍然是 alpha 版本,但这正是我喜欢测试的那种功能,我建议您也测试一下,以帮助它尽快进入下一个质量级别。
让我们一探究竟。
目标
在这篇文章中,我想测试:
- 如何在 MariaDB 中启用 DuckDB 存储引擎
- 如何创建一个 DuckDB 表
- 如何加载一些数据
- 如何查询数据
- 如何连接 InnoDB 表和 DuckDB 表
- 我们能多大程度上使用 DuckDB 的特定功能,比如 Parquet
- 一些限制
安装
在首次测试中,我使用了 MariaDB 的 CI 和 dbdeployer 提供的包。
请记住,这些包是未签名的,仅用于测试。
$ wget https://ci.mariadb.org/68929/amd64-fedora-42/mariadb-11.4.13-linux-x86_64.tar.gz
$ dbdeployer unpack mariadb-11.4.13-linux-x86_64.tar.gz
安装服务器后,我们需要通过修改配置文件(dbdeployer 沙箱中的 my.sandbox.cnf)在启动时加载插件:
plugin-maturity=alpha
plugin-load-add=ha_duckdb.so
duckdb-memory-limit=8G
plugin-maturity=alpha 这一行是必需的,因为该插件目前标记为 alpha 版本。
我还增加了 duckdb-memory-limit,因为默认值只有 1G,在加载或查询较大数据集时,这会很快成为第一个限制因素。
我们可以重启实例并检查错误日志,确认插件已正确加载:
$ ./restart
$ ./show_log | tail -n 11
2026-06-11 12:44:52 0 [Note] InnoDB: Buffer pool(s) load completed at 260611 12:44:52
2026-06-11 12:44:52 0 [Note] DuckDB: registered MySQL-compatible function overloads
(octet_length, length, ascii, ord, hex, oct, bin, locate, mid, rtrim,
ltrim, regexp_instr, regexp_replace, regexp_substr, json_unquote,
json_contains)
2026-06-11 12:44:52 0 [Note] DuckDB: cross-engine scan registered (_mdb_scan + replacement scan)
2026-06-11 12:44:52 0 [Note] DuckDB: DuckdbManager::Initialize succeed, path=/var/run/media/fred/DATA/DBDEPLOYER/sandboxes/msb_11_4_13/data/duckdb.db
2026-06-11 12:44:52 0 [Note] DuckDB storage engine initialized
2026-06-11 12:44:52 0 [Note] Plugin 'FEEDBACK' is disabled.
2026-06-11 12:44:52 0 [Note] Plugin 'wsrep-provider' is disabled.
2026-06-11 12:44:52 0 [Note] Server socket created on IP: '127.0.0.1', port: '11413'.
2026-06-11 12:44:52 0 [Note] mariadbd: Event Scheduler: Loaded 0 events
2026-06-11 12:44:52 0 [Note] /var/run/media/fred/DATA/DBDEPLOYER/opt/11.4.13/bin/mariadbd: ready for connections.
Version: '11.4.13-MariaDB' socket: '/tmp/mysql_sandbox11413.sock' port: 11413 Source distribution
我们可以使用 mariadb 客户端连接到实例,并使用 SHOW ENGINES\G 检查引擎,应该会看到以下内容:
...
*************************** 7. row ***************************
Engine: DUCKDB
Support: YES
Comment: DuckDB storage engine
Transactions: YES
XA: YES
Savepoints: NO
我们还可以看到专用于 DuckDB 的新的全局变量和状态变量:
mariadb > show global variables like 'duc%';
+-------------------------------------------+---------------------+
| Variable_name | Value |
+-------------------------------------------+---------------------+
| duckdb_appender_allocator_flush_threshold | 67108864 |
| duckdb_checkpoint_threshold | 268435456 |
| duckdb_copy_ddl_in_batch | ON |
| duckdb_disabled_optimizers | |
| duckdb_dml_in_batch | ON |
| duckdb_explain_output | PHYSICAL_ONLY |
| duckdb_force_no_collation | OFF |
| duckdb_log_options | |
| duckdb_max_temp_directory_size | 0 |
| duckdb_max_threads | 0 |
| duckdb_memory_limit | 8589934592 |
| duckdb_merge_join_threshold | 4611686018427387904 |
| duckdb_require_primary_key | ON |
| duckdb_scheduler_process_partial | ON |
| duckdb_temp_directory | |
| duckdb_update_modified_column_only | ON |
| duckdb_use_direct_io | OFF |
| duckdb_use_double_for_decimal | ON |
+-------------------------------------------+---------------------+
18 rows in set (0.002 sec)
mariadb > show global status like '%duck%';
+-----------------------------+-------+
| Variable_name | Value |
+-----------------------------+-------+
| Duckdb_commit | 0 |
| Duckdb_rollback | 0 |
| Duckdb_rows_delete | 0 |
| Duckdb_rows_delete_in_batch | 0 |
| Duckdb_rows_insert | 0 |
| Duckdb_rows_insert_in_batch | 0 |
| Duckdb_rows_update | 0 |
| Duckdb_rows_update_in_batch | 0 |
+-----------------------------+-------+
8 rows in set (0.002 sec)
一切顺利,我们可以继续下一步了!
创建 DuckDB 表
我的第一个测试是创建一个 DuckDB 表:
mariadb > CREATE DATABASE ducktest;
mariadb > USE ducktest;
mariadb > CREATE TABLE sales (
id BIGINT PRIMARY KEY,
product VARCHAR(64),
amount DECIMAL(12,2),
sold_at TIMESTAMP ) ENGINE=DuckDB;
ERROR 1478 (HY000): Table storage engine 'DuckDB' does not support the create option 'non-utf8 charset'
这个错误是预期的,正如 Roman 在他的公告中所说,DuckDB 拒绝非 UTF-8 字符集。11.4 版本的默认字符集是 latin1。
mariadb > CREATE TABLE sales (
id BIGINT PRIMARY KEY,
product VARCHAR(64),
amount DECIMAL(12,2),
sold_at TIMESTAMP ) ENGINE=DuckDB DEFAULT CHARSET utf8mb4;
Query OK, 0 rows affected (0.014 sec)
让我们添加一些行并尝试一些查询:
mariadb > INSERT INTO sales VALUES (1, 'MariaDB mug', 12.00, NOW()),
(2, 'DuckDB sticker', 2.50, NOW()),
(3, 'Sea lion plush', 25.00, NOW()),
(4, 'DuckDB sticker', 2.50, NOW());
Query OK, 4 rows affected (0.007 sec)
Records: 4 Duplicates: 0 Warnings: 0
mariadb > SELECT * FROM sales;
+----+----------------+--------+---------------------+
| id | product | amount | sold_at |
+----+----------------+--------+---------------------+
| 1 | MariaDB mug | 12.00 | 2026-06-11 12:49:44 |
| 2 | DuckDB sticker | 2.50 | 2026-06-11 12:49:44 |
| 3 | Sea lion plush | 25.00 | 2026-06-11 12:49:44 |
| 4 | DuckDB sticker | 2.50 | 2026-06-11 12:49:44 |
+----+----------------+--------+---------------------+
4 rows in set (0.007 sec)
mariadb > SELECT product, SUM(amount) AS revenue
FROM sales GROUP BY product ORDER BY revenue DESC;
+----------------+---------+
| product | revenue |
+----------------+---------+
| Sea lion plush | 25.00 |
| MariaDB mug | 12.00 |
| DuckDB sticker | 5.00 |
+----------------+---------+
3 rows in set (0.007 sec)
mariadb > SELECT p.category, SUM(s.amount) AS revenue
FROM sales s
JOIN products p ON p.product = s.product
GROUP BY p.category ORDER BY revenue DESC;
+----------+---------+
| category | revenue |
+----------+---------+
| toys | 25.00 |
| goodies | 17.00 |
+----------+---------+
2 rows in set (0.003 sec)
到目前为止,从 SQL 用户的角度来看,没有什么特别的。
而这正是关键所在!
混合使用 InnoDB 和 DuckDB
真正有趣的是,我们可以在同一个 schema 中使用两种引擎。
让我们创建一个小型的 InnoDB 表:
mariadb > CREATE TABLE products (
product VARCHAR(64) PRIMARY KEY,
category VARCHAR(64)
) ENGINE=InnoDB;
mariadb > INSERT INTO products VALUES ('MariaDB mug', 'goodies'),
('DuckDB sticker', 'goodies'),
('Sea lion plush', 'toys');
这就是这个引擎变得非常有趣的地方。
我们可以想象将操作数据保留在 InnoDB 中,并将分析或历史数据移动到 DuckDB 表中。
AirportDB
让我们用一个更大的数据集来测试 DuckDB。
我加载了一个对于那些测试过 MySQL HeatWave 的人来说很熟悉的数据集:airportdb。
我无法在 MariaDB 中加载 mysqlsh 转储,因此不得不加载一个逻辑 SQL 转储。
最大的表(booking)被加载到 InnoDB 中,另一个副本加载到 DuckDB 中以供比较。
InnoDB 的加载过程几乎是没完没了的……我没想到会这么长,所以我没有测量时间,但真的是非~~~~常长。DuckDB 的导入则快得多!
为了进行公平的比较,我将 duckdb_memory_limit 降低到 4GB,并将 innodb_buffer_pool_size 也增加到 4GB。
加载完成后我重启了 MariaDB 服务器。
mariadb > SELECT count(*) FROM booking;
+----------+
| count(*) |
+----------+
| 54304619 |
+----------+
1 row in set (26.656 sec)
mariadb > SELECT count(*) FROM booking_duck;
+----------+
| count(*) |
+----------+
| 54304619 |
+----------+
1 row in set (0.005 sec)
我们已经可以看到很大的差异。
让我们比较一个查询,首先在 InnoDB 中:
mariadb > SELECT a.airlinename, COUNT(*) AS bookings
FROM booking b JOIN flight f ON b.flight_id = f.flight_id
JOIN airline a ON f.airline_id = a.airline_id
GROUP BY a.airlinename ORDER BY bookings DESC LIMIT 10;
+----------------------+----------+
| airlinename | bookings |
+----------------------+----------+
| Vanuatu Airlines | 743353 |
| Peru Airlines | 703840 |
| Micronesia Airlines | 700704 |
| Yugoslavia Airlines | 697159 |
| Hungary Airlines | 693488 |
| Falkland Is Airlines | 666531 |
| Ethiopia Airlines | 660362 |
| Tunisia Airlines | 638740 |
| Swaziland Airlines | 602344 |
| Puerto Rico Airlines | 602130 |
+----------------------+----------+
10 rows in set (27.973 sec)
现在在 DuckDB 中(也连接了 InnoDB 表):
mariadb > SELECT a.airlinename, COUNT(*) AS bookings
FROM booking_duck b JOIN flight f ON b.flight_id = f.flight_id
JOIN airline a ON f.airline_id = a.airline_id
GROUP BY a.airlinename ORDER BY bookings DESC LIMIT 10;
+----------------------+----------+
| airlinename | bookings |
+----------------------+----------+
| Vanuatu Airlines | 743353 |
| Peru Airlines | 703840 |
| Micronesia Airlines | 700704 |
| Yugoslavia Airlines | 697159 |
| Hungary Airlines | 693488 |
| Falkland Is Airlines | 666531 |
| Ethiopia Airlines | 660362 |
| Tunisia Airlines | 638740 |
| Swaziland Airlines | 602344 |
| Puerto Rico Airlines | 602130 |
+----------------------+----------+
10 rows in set (0.456 sec)
毫无悬念!
磁盘大小
如果我们检查磁盘消耗,可以看到对于 booking 表,InnoDB 的表空间是 7.2GB:
$ ls -lh booking*
-rw-rw---- 1 fred fred 2.5K Jun 11 13:53 booking_duck.frm
-rw-rw---- 1 fred fred 2.5K Jun 11 14:05 booking.frm
-rw-rw---- 1 fred fred 7.2G Jun 11 17:47 booking.ibd
DuckDB 的数据占用的空间要小得多:
$ ls -lh duck*
-rw-rw---- 1 fred fred 333M Jun 12 08:42 duckdb.db
-rw-rw---- 1 fred fred 8.1K Jun 12 09:47 duckdb.db.wal
这也是非常重要的一点,这是一个显著的优势!
几乎小了 22 倍,减少了 95.4% 的大小!
一些限制
在我的测试中,我遇到了一些限制,但请不要忘记这仍然是一个 Alpha 版本:
auto_increment
目前,不支持 auto_increment。当然,因为主要目的是将现有数据从 InnoDB 迁移到 DuckDB 进行分析,主键不会改变,因此不需要生成它。
但在其他情况下,支持 auto_increment 也是不错的。
mariadb > CREATE TABLE invoice (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
product VARCHAR(64),
amount DECIMAL(12,2),
sold_at TIMESTAMP ) ENGINE=DuckDB DEFAULT CHARSET utf8mb4;
ERROR 1164 (42000): Storage engine DUCKDB doesn't support AUTO_INCREMENT columns
好吧,让我们看看是否有其他替代方案。
首先尝试使用 SEQUENCE:
mariadb > CREATE SEQUENCE primary_key_seq START WITH 1 INCREMENT BY 1 CACHE 100;
Query OK, 0 rows affected (0.014 sec)
mariadb > CREATE TABLE invoice (
id BIGINT PRIMARY KEY DEFAULT(NEXT VALUE FOR primary_key_seq),
product VARCHAR(64),
amount DECIMAL(12,2),
sold_at TIMESTAMP ) ENGINE=DuckDB DEFAULT CHARSET utf8mb4;
ERROR 1296 (HY000): Got error 168 'Binder Error: DEFAULT value cannot contain column names
LINE 1: ...(nextval("ducktest"."primary_key_seq")),"product" VARCHAR COLLATE NOCASE.NOACCENT,"amount" DECIMAL(12,2),"sold_at" TIMESTA...
' from DuckDB
这不被支持,那么让我们尝试其他方法:UUID
mariadb > CREATE TABLE invoice (
id UUID DEFAULT(uuid()) PRIMARY KEY,
product VARCHAR(64),
amount DECIMAL(12,2),
sold_at TIMESTAMP ) ENGINE=DuckDB DEFAULT CHARSET utf8mb4;
Query OK, 0 rows affected (0.016 sec)
它似乎有效……但是……
mariadb > INSERT INTO invoice (product, amount, sold_at)
VALUES ('MariaDB mug', 12.00, NOW()),
('DuckDB sticker', 2.50, NOW()),
('Sea lion plush', 25.00, NOW()),
('DuckDB sticker', 2.50, NOW());
Query OK, 4 rows affected (0.009 sec)
Records: 4 Duplicates: 0 Warnings: 0
mariadb > SELECT id, product, amount FROM invoice;
+--------------------------------------+----------------+--------+
| id | product | amount |
+--------------------------------------+----------------+--------+
| 00000000-0000-0000-0000-000000000000 | MariaDB mug | 12.00 |
| 00000000-0000-0000-0000-000000000000 | DuckDB sticker | 2.50 |
| 00000000-0000-0000-0000-000000000000 | Sea lion plush | 25.00 |
| 00000000-0000-0000-0000-000000000000 | DuckDB sticker | 2.50 |
+--------------------------------------+----------------+--------+
4 rows in set (0.003 sec)
我们可以看到 uuid() 并没有按预期生成。但这破坏了主键约束。
这似乎是 UUID 数据类型的一个问题,因为即使提供了值,它仍然是 ‘00000000-0000-0000-0000-000000000000’:
mariadb > insert into invoice values ('65297af6-6630-11f1-8ad2-5e1b9081e705','fred',10, now());
Query OK, 1 row affected (0.011 sec)
mariadb > SELECT id, product, amount FROM invoice;
+--------------------------------------+----------------+--------+
| id | product | amount |
+--------------------------------------+----------------+--------+
| 00000000-0000-0000-0000-000000000000 | MariaDB mug | 12.00 |
| 00000000-0000-0000-0000-000000000000 | DuckDB sticker | 2.50 |
| 00000000-0000-0000-0000-000000000000 | Sea lion plush | 25.00 |
| 00000000-0000-0000-0000-000000000000 | DuckDB sticker | 2.50 |
| 00000000-0000-0000-0000-000000000000 | fred | 10.00 |
+--------------------------------------+----------------+--------+
5 rows in set (0.002 sec)
因此,最好在插入记录时提供主键值,但要避免使用 UUID。
在 DuckDB 中,不支持自增,但支持序列。这些序列与 MariaDB 的不同,是 DuckDB 内部的。我尝试在 DuckDB 中创建一个序列,它起作用了,但我还不能从 MariaDB 使用它。这需要进一步研究。
DuckDB 的范围是以非常快的速度批量摄取大量数据并提供非常快速的查询,而不是逐行插入。
Insert from select
将数据从 InnoDB 表插入到 DuckDB 表可以按预期工作。但是,反向操作则不受支持:
mariadb > INSERT INTO invoice2 (SELECT * FROM invoice);
ERROR 1296 (HY000): Got error 122 'Catalog Error: Table with name invoice2 does not exist!
Did you mean "invoice"?' from DuckDB
你可能会想先创建第二个表作为 DuckDB 表,然后将其改为 InnoDB。但这不会按预期工作:
mariadb > CREATE TABLE invoice2 ( id INT UNSIGNED PRIMARY KEY,
product VARCHAR(64),
amount DECIMAL(12,2),
sold_at TIMESTAMP ) engine=DuckDB default CHARSET utf8mb4;
Query OK, 0 rows affected (0.017 sec)
mariadb > INSERT INTO invoice2 (SELECT * FROM invoice);
Query OK, 1 row affected (0.009 sec)
Records: 1 Duplicates: 0 Warnings: 0
mariadb > SELECT * FROM invoice2;
+----+----------------+--------+---------------------+
| id | product | amount | sold_at |
+----+----------------+--------+---------------------+
| 1 | MariaDB mug | 12.00 | 2026-06-12 09:42:25 |
| 2 | DuckDB sticker | 2.50 | 2026-06-12 09:42:25 |
| 3 | Sea lion plush | 25.00 | 2026-06-12 09:42:25 |
| 4 | DuckDB sticker | 2.50 | 2026-06-12 09:42:25 |
| 4 | NULL | NULL | NULL |
+----+----------------+--------+---------------------+
5 rows in set (0.004 sec)
我们可以看到最后多了一行;在将表更改为 InnoDB 之前,我们必须删除它,因为主键约束会失败(该值不唯一)。
子查询
在使用 airportdb 时,我喜欢用一个查询作为例子;然而,这个查询目前还不能工作:
mariadb > SELECT airlinename, sum(sold_seat)/sum(capacity) 'load factor'
FROM (
SELECT flight_id, COUNT(*) sold_seat
FROM booking_duck GROUP BY flight_id) seat_sold
JOIN flight using (flight_id)
JOIN airplane USING (airplane_id)
JOIN airline ON airline.airline_id = flight.airline_id
GROUP BY airlinename ORDER BY airlinename LIMIT 10;
ERROR 1296 (HY000): Got error 122 'Parser Error: Expected a constant as type modifier' from DuckD
更快地加载数据
现在回到 DuckDB 擅长的领域:加载数据。
可以通过 run_in_duckdb() 函数使用 DuckDB 原生的 COPY。这可以避免通过普通的 MariaDB SQL 层逐条推送所有行。
$ ls -lh booking.csv
-rw-r--r-- 1 fred fred 2.2G Jun 12 09:57 booking.csv
首先,我们创建表:
mariadb > CREATE TABLE `booking2` (
`booking_id` int(11) NOT NULL,
`flight_id` int(11) NOT NULL,
`seat` char(4) DEFAULT NULL,
`passenger_id` int(11) NOT NULL,
`price` decimal(10,2) NOT NULL,
PRIMARY KEY (`booking_id`),
UNIQUE KEY `seatplan_unq` (`flight_id`,`seat`),
KEY `flight_idx` (`flight_id`),
KEY `passenger_idx` (`passenger_id`)
) ENGINE=DUCKDB DEFAULT CHARSET=utf8mb4;
Query OK, 0 rows affected (0.012 sec)
然后,我们使用超级函数 run_in_duckdb() 加载 CSV 文件:
mariadb > SELECT run_in_duckdb("COPY airport.booking2 FROM 'booking.csv' (HEADER, DELIMITER ',') ")\G
*************************** 1. row ***************************
run_in_duckdb("COPY airport.booking2 FROM 'booking.csv' (HEADER, DELIMITER ',') "): Count
BIGINT
[ Rows: 1]
54304619
1 row in set (4.104 sec)
mariadb > SELECT count(*) FROM booking2;
+----------+
| count(*) |
+----------+
| 54304619 |
+----------+
1 row in set (0.013 sec)
速度非常快:54,304,619 行只用了 4 秒!!
导入 Parquet 文件也是可以的!
将 DuckDB 表导出到 Parquet
当我在社交媒体上分享 DuckDB 的公告时,得到的第一个评论是是否可以将数据备份到 Parquet 文件。
好消息是:是的,可以!
再一次,run_in_duckdb() 函数是我们所需要的:
mariadb > SELECT run_in_duckdb(" COPY airport.booking_duck TO 'booking_backup.parquet' (FORMAT parquet) ")\G
*************************** 1. row ***************************
run_in_duckdb(" COPY airport.booking_duck TO 'booking_backup.parquet' (FORMAT parquet) "): Count
BIGINT
[ Rows: 1]
54304619
1 row in set (1.597 sec)
让我们检查生成的文件:
$ ls -lh booking_backup.parquet
-rw-rw---- 1 fred fred 703M Jun 12 10:28 booking_backup.parquet
$ file booking_backup.parquet
booking_backup.parquet: Apache Parquet
我们可以验证它:
$ parquet-tools inspect booking_backup.parquet
###### file meta data ######
created_by: DuckDB version v1.5.2 (build 0000000000)
num_columns: 5
num_rows: 54304619
num_row_groups: 442
format_version: 1.0
serialized_size: 187066
###### Columns ######
booking_id
flight_id
seat
passenger_id
price
###### Column(booking_id) ######
name: booking_id
path: booking_id
max_definition_level: 1
max_repetition_level: 0
physical_type: INT32
logical_type: Int(bitWidth=32, isSigned=true)
converted_type (legacy): INT_32
compression: SNAPPY (space_saved: -0%)
###### Column(flight_id) ######
name: flight_id
path: flight_id
max_definition_level: 1
max_repetition_level: 0
physical_type: INT32
logical_type: Int(bitWidth=32, isSigned=true)
converted_type (legacy): INT_32
compression: SNAPPY (space_saved: -0%)
###### Column(seat) ######
name: seat
path: seat
max_definition_level: 1
max_repetition_level: 0
physical_type: BYTE_ARRAY
logical_type: String
converted_type (legacy): UTF8
compression: SNAPPY (space_saved: 1%)
###### Column(passenger_id) ######
name: passenger_id
path: passenger_id
max_definition_level: 1
max_repetition_level: 0
physical_type: INT32
logical_type: Int(bitWidth=32, isSigned=true)
converted_type (legacy): INT_32
compression: SNAPPY (space_saved: -0%)
###### Column(price) ######
name: price
path: price
max_definition_level: 1
max_repetition_level: 0
physical_type: INT64
logical_type: Decimal(precision=10, scale=2)
converted_type (legacy): DECIMAL
compression: SNAPPY (space_saved: 47%)
也可以将查询结果导出到 Parquet 文件,但查询只能包含 DuckDB 中的表,不能包含其他存储引擎(如 InnoDB)的表:
mariadb > SELECT run_in_duckdb(" COPY ( SELECT flight_id, AVG(price) AS price_avg
FROM airport.booking_duck
GROUP BY flight_id order by price_avg DESC ) TO
'booking_prices.parquet' (FORMAT parquet, COMPRESSION zstd)")\G
*************************** 1. row ***************************
run_in_duckdb(" COPY ( SELECT flight_id, AVG(price) AS price_avg
FROM airport.booking_duck
GROUP BY flight_id order by price_avg DESC ) TO 'booking_prices.parquet' (FORMAT parquet, COMPRESSION zstd)"): Count
BIGINT
[ Rows: 1]
462553
1 row in set (0.123 sec)
仅供参考
DuckDB 是一个专为分析设计的轻量级 SQL 数据库引擎。
可以把它想象成用于数据分析的 SQLite。
它非常有用,因为它可以直接查询文件,包括:
- CSV
- Parquet
- JSON
- Arrow
如果 MariaDB 服务器没有运行,否则会有冲突锁。你可以在 MariaDB 的 datadir 目录下,使用 duckdb 的 Python 模块来处理你的 duckdb.db 文件。
要小心,你可能会破坏一切!
$ python3 - <<'PY'
import duckdb
con = duckdb.connect("duckdb.db")
print(con.sql("SHOW SCHEMAS"))
PY
┌───────────────┬─────────────┬─────────┐
│ database_name │ schema_name │ current │
│ varchar │ varchar │ boolean │
├───────────────┼─────────────┼─────────┤
│ duckdb │ airport │ false │
│ duckdb │ ducktest │ false │
│ duckdb │ main │ true │
└───────────────┴─────────────┴─────────┘
你也可以在本地安装 DuckDB,然后你会得到 CLI 客户端:
$ curl https://install.duckdb.org | sh
*** DuckDB Linux/MacOS installation script, version 1.5.3 ***
.;odxdl,
.xXXXXXXXXKc
0XXXXXXXXXXXd cooo:
,XXXXXXXXXXXXK OXXXXd
0XXXXXXXXXXXo cooo:
.xXXXXXXXXKc
.;odxdl,
#################################### 100.0%
Successfully installed DuckDB 1.5.3 to /home/fred/.duckdb/cli/1.5.3/duckdb
Updated symlink from /home/fred/.duckdb/cli/latest/duckdb to
/home/fred/.duckdb/cli/1.5.3/duckdb
Hint: Append the following line to your shell profile:
export PATH="/home/fred/.duckdb/cli/latest":$PATH
Also created a symlink from /home/fred/.local/bin/duckdb
to /home/fred/.duckdb/cli/latest/duckdb
To launch DuckDB 1.5.3 now, type
/home/fred/.duckdb/cli/latest/duckdb
这允许你连接到你的数据库:
$ duckdb duckdb.db
DuckDB v1.5.3 (Variegata)
Enter ".help" for usage hints.
duckdb D .databases
┌─────────────────────────────────────────────────────────────────────────────┐
│ databases │
│ │
│ duckdb /run/media/fred/data/dbdeployer/sandboxes/msb_11_4_13/data/duckdb.db │
└─────────────────────────────────────────────────────────────────────────────┘
我们能做得更多吗?
但我们还可以更进一步:假设我们想使用 Arrow 文件而不是 Parquet。这也是可行的。
你需要安装插件,加载它,然后使用它。如果你的系统上的 DuckDB 版本与 MariaDB 中的版本相同,你可以直接从 duckdb 客户端 CLI 管理插件。
最新版本是 1.5.3,而与 MariaDB 一起构建的版本是 1.5.2。这意味着最简单的方法是使用超级函数 run_in_duckdb():
mariadb > SELECT run_in_duckdb("INSTALL nanoarrow FROM community");
*************************** 1. row ***************************
run_in_duckdb("INSTALL nanoarrow FROM community")
Success
BOOLEAN
[ Rows: 0]
1 row in set (0.881 sec)
mariadb > SELECT run_in_duckdb("LOAD nanoarrow");
*************************** 1. row ***************************
run_in_duckdb("LOAD nanoarrow")
Success
BOOLEAN
[ Rows: 0]
1 row in set (0.034 sec)
mariadb > SELECT run_in_duckdb(" COPY airport.booking_duck TO 'booking_backup.arrow' (FORMAT arrow) ")\G
*************************** 1. row ***************************
run_in_duckdb(" COPY airport.booking_duck TO 'booking_backup.arrow' (FORMAT arrow) "): Count
BIGINT
[ Rows: 1]
54304619
1 row in set (2.185 sec)
现在我们有了导出的文件:
ls -lh booking_backup.arrow
-rw-rw---- 1 fred fred 1.8G Jun 12 13:25 booking_backup.arrow
我们可以验证它是正确的:
$ duckdb
DuckDB v1.5.3 (Variegata)
Enter ".help" for usage hints.
memory D LOAD nanoarrow;
memory D SELECT * FROM read_arrow('booking_backup.arrow') LIMIT 10;
┌────────────┬───────────┬─────────┬──────────────┬───────────────┐
│ booking_id │ flight_id │ seat │ passenger_id │ price │
│ int32 │ int32 │ varchar │ int32 │ decimal(10,2) │
├────────────┼───────────┼─────────┼──────────────┼───────────────┤
│ 1 │ 3863 │ NULL │ 2947 │ 110.10 │
│ 2 │ 3863 │ 25F │ 20814 │ 173.10 │
│ 3 │ 3863 │ 25E │ 33340 │ 323.15 │
│ 4 │ 3863 │ 24D │ 21401 │ 445.85 │
│ 5 │ 3863 │ 24G │ 24581 │ 198.32 │
│ 6 │ 3863 │ 24F │ 16902 │ 275.88 │
│ 7 │ 3863 │ 23E │ 31818 │ 170.03 │
│ 8 │ 3863 │ 22F │ 1878 │ 221.48 │
│ 9 │ 3863 │ 22C │ 16535 │ 264.82 │
│ 10 │ 3863 │ 21A │ 32301 │ 131.94 │
└────────────┴───────────┴─────────┴──────────────┴───────────────┘
10 rows 5 columns
结论
我的第一印象是我非常喜欢它!
MariaDB 已经有多个存储引擎,而这个引擎带来了不同的东西:在同一个服务器内部使用一个非常流行的分析引擎进行轻量级分析处理。
DuckDB 存储引擎对于 MariaDB 来说确实是 MariaDB 生态系统一个非常有趣的补充。
它不会取代 InnoDB,也可能不会取代 ColumnStore。
但它可能成为一个非常好的解决方案,用于本地分析、HTAP 实验、报表表和数据湖交互。
对我来说,最有趣的技巧是能够使用 run_in_duckdb() 来访问那些尚未通过 MariaDB SQL 解析器提供的 DuckDB 功能。
是的,这包括 Parquet。
所以现在我们有了一个会嘎嘎叫的海狮……还能导出 Parquet。
尽情享受 MariaDB 和 DuckDB 吧!


被折叠的 条评论
为什么被折叠?



