DuckDB
以 pgcli 插件方式安装嵌入式 OLAP 分析数据库(宿主二进制)。
DuckDB 是嵌入式分析数据库——常被称作"SQLite for analytics"。
C++ 编写的列式引擎,向量化执行,可以直接查询 Parquet、CSV、JSON 和 Arrow 文件,
无需服务器进程。把它当成一个单二进制的 OLAP 引擎,指向你的数据即可使用:
加载 httpfs 扩展后,SELECT * FROM read_parquet('s3://bucket/data.parquet')
开箱即用。
作为 pgcli 插件,DuckDB 是宿主二进制形态:没有容器,没有守护进程。
pg addon install duckdb 从 GitHub releases 下载官方 CLI zip(sha256 校验;
PATH 上已有 duckdb 则直接复用),安装 httpfs 扩展(parquet 和 json
已内置于二进制中),并在 pg.yaml 中记录条目。
平台:仅 Linux,仅 amd64(macOS 及其他架构明确拒绝并给出错误提示)。
DuckDB 是一个独立的 CLI 工具——不作为后台服务运行、不监听端口、不会传播进
PG 实例容器。
为什么在 pgcli 旁边装 DuckDB
DuckDB 填补了 PostgreSQL 行存引擎不擅长的分析场景:对大量 Parquet/CSV 数据集的
即席查询、数据湖探索、加载进 PostgreSQL 之前的 ETL 预处理,或者对已经躺在 S3 里
的文件做快速聚合。pgcli 已经管理了多种 S3 存储(MinIO、silo、rustfs、garage、
seaweedfs)——DuckDB 通过 httpfs 直接查询其中的内容。
用法
# 默认:v1.5.6 LTS,安装 httpfs 扩展(parquet/json 已内置)
pg addon install duckdb
# 指定版本
pg addon install duckdb --version 2.0.0
# 自定义名称和数据目录
pg addon install duckdb --name analytics --data-dir /srv/duckdb
# 跳过扩展安装
pg addon install duckdb --no-extensions
# 重新安装并更换参数(条目已存在时)
pg addon install duckdb --version 2.0.0 --force
安装完成后,启动交互式 shell 或执行一次性查询:
# 交互式 shell(数据目录作为工作目录)
pg addon duckdb shell
# 对本地 Parquet 文件的一次性查询
pg addon duckdb shell -- -c "SELECT * FROM read_parquet('/tmp/data.parquet')"
# 直接查询 S3(需要 httpfs 扩展,默认已安装)
duckdb -c "SELECT count(*) FROM read_parquet('s3://bucket/data/*.parquet')"
# 打开持久化数据库文件
duckdb ~/.pgcli/addon/duckdb/duckdb/analytics.duckdb
参数
日常管理
pg addon duckdb status --name duckdb # 二进制版本、路径、数据目录
pg addon duckdb shell --name duckdb # 交互式 DuckDB 会话
pg addon duckdb shell -- -c "SELECT 1" # 一次性查询(-- 后面的参数传给 duckdb)
pg addon list # duckdb 显示在"Analytics add-ons"下
pg addon start duckdb # 无操作(CLI 工具,非守护进程)
pg addon stop duckdb # 无操作(CLI 工具,非守护进程)
pg addon remove duckdb --name duckdb # 忘掉配置;二进制和数据目录保留
pg addon remove duckdb --name duckdb --clean-data # 同时删除数据目录
remove 从不删除二进制——它可能被多个命名实例共享。加 --clean-data 删除
数据目录(数据库文件);不加则保留目录并打印提示。
Server/Client 模式(v2.0.0+)
DuckDB v2.0.0 引入了 Quack 协议,支持原生 server/client 模式。
DuckDB 可以通过 HTTP 对外提供数据库服务,多个客户端可以同时查询同一个实例。
启动服务端
# 交互式 server(stdin 必须保持打开)
duckdb /path/to/db.duckdb -serve
# 后台 server(重定向 stdin 以保持存活)
nohup duckdb /path/to/db.duckdb -serve < /dev/zero > /tmp/duckdb-serve.log 2>&1 &
# 或在 SQL 中使用 quack_serve()
duckdb -c "INSTALL quack; LOAD quack; CALL quack_serve(token='mytoken')"
服务端默认监听 localhost:9494,启动时会打印自动生成的 auth token。
默认只接受本地连接——如需远程访问,需在前面放一个 TLS 终止反向代理
(参见 DuckDB 文档)。
跨主机访问(绑定 0.0.0.0)
要接受来自任意网络接口的连接,使用 quack_serve() 并指定参数(而非 -serve):
-- 服务端:绑定到所有接口的 9494 端口
CALL quack_serve(
listen_uri='quack:0.0.0.0:9494',
allow_other_hostname=true, -- 必须,允许非 localhost 绑定
disable_ssl=true, -- 可选,默认是 HTTPS
token='mytoken'
);
需要三个参数:
listen_uri='quack:0.0.0.0:9494' — 绑定到所有接口(不只是 localhost)
allow_other_hostname=true — 绕过仅限 localhost 的限制
disable_ssl=true — 使用纯 HTTP(默认是 HTTPS 并自动生成证书)
客户端使用服务端 IP 地址连接:
# 客户端连接远程服务端
duckdb -c "ATTACH 'quack:192.0.2.10:9494' AS qk (TOKEN 'mytoken'); CONNECT qk; SELECT * FROM t"
安全警告:绑定 0.0.0.0 并使用 disable_ssl=true 会将数据库以明文形式暴露到网络。
仅在可信网络或防火墙后面使用。生产环境请使用带 TLS 的反向代理。
客户端连接
# 使用 -connect 参数(v2.0.0+)
duckdb -connect -c "SELECT * FROM t"
# 或通过 SQL(简写形式,默认 localhost)
duckdb -c "ATTACH 'quack:localhost' AS qk (TOKEN 'BAED...'); CONNECT qk; SELECT * FROM t"
# 显式 ip:port 形式(远程连接或非默认端口时使用)
duckdb -c "ATTACH 'quack:127.0.0.1:9494' AS qk (TOKEN 'BAED...'); CONNECT qk; SELECT * FROM t"
-connect 参数会自动发现服务端输出的 token。手动连接时,从服务端启动
信息中复制 token 即可。
注意事项
- stdin 必须保持打开:服务端在 stdin 关闭后会退出(后台运行用
< /dev/zero 或 tail -f /dev/null |)
- 仅限本地:远程访问需要带 TLS 终止的反向代理
- Auth token:自动生成,启动时打印,客户端连接时必须提供
- pgcli 不管理 Quack server:
pg addon start/stop duckdb 仍然是无操作——Quack server 是一个独立进程,需要自行管理
扩展
DuckDB 的扩展系统在首次使用时安装共享库。pgcli 在安装时预装 httpfs——
它启用通过 HTTP/S3 读取文件的能力。parquet 和 json 是静态链接在 DuckDB CLI
二进制里的,始终可用,无需网络下载。
httpfs —— 通过 HTTP/S3 读取文件(read_parquet('s3://...'))
parquet —— Parquet 文件读写器(内置)
json —— JSON 文件读取器(read_json_auto(...))(内置)
后续可以从 DuckDB shell 里安装更多扩展:
INSTALL 'spatial'; -- 地理空间函数
INSTALL 'sqlite'; -- 读写 SQLite 数据库
INSTALL 'mysql'; -- 直接查询 MySQL
LOAD 'httpfs'; -- 在当前会话中加载已安装的扩展
如果希望手动管理扩展,或安装时主机无法上网,可在安装时加 --no-extensions。
访问 S3、Parquet、JSON 和 CSV 数据
DuckDB 天生为文件分析查询而生——无需服务器、无需 COPY、无需导入步骤。
直接指向你的数据即可。
Parquet 文件
-- 单个文件
SELECT * FROM read_parquet('/tmp/events.parquet');
-- 通配符:目录下所有 parquet 文件
SELECT * FROM read_parquet('/data/logs/*.parquet');
-- schema 推断 + 计数
SELECT count(*) FROM read_parquet('s3://bucket/year=2026/*.parquet');
-- 指定列 + 过滤
SELECT user_id, event, ts
FROM read_parquet('/data/events.parquet')
WHERE ts > '2026-01-01'
LIMIT 100;
JSON 文件
-- 自动推断 schema(支持 JSON lines 和嵌套 JSON)
SELECT * FROM read_json_auto('/tmp/users.json');
-- 显式 schema
SELECT * FROM read_json('/tmp/users.json',
columns = {'id': 'INTEGER', 'name': 'VARCHAR', 'email': 'VARCHAR'},
format = 'newline_delimited');
-- 嵌套对象:用 ->> 提取字段
SELECT
data->>'user_id' AS user_id,
data->>'event' AS event
FROM read_json_auto('/tmp/events.jsonl');
CSV 文件
-- 自动推断表头和类型
SELECT * FROM read_csv_auto('/tmp/sales.csv');
-- 显式列类型(自动推断不准时)
SELECT * FROM read_csv('/tmp/sales.csv',
columns = {'id': 'INTEGER', 'amount': 'DOUBLE', 'date': 'DATE'},
header = true);
-- TSV 或自定义分隔符
SELECT * FROM read_csv_auto('/tmp/data.tsv', delim = '\t');
S3 访问(httpfs 扩展)
httpfs 扩展启用 S3 访问。凭据和端点通过 CREATE SECRET(v2.0.0+)或传统
SET 命令(v1.5.x)配置。
DuckDB v2.0.0+ — CREATE SECRET
-- 配置 S3 凭据(v2.0.0+ 语法)
CREATE SECRET (
TYPE S3,
ENDPOINT 'minio.example.com:9000',
KEY_ID 'admin',
SECRET 'your-secret-key',
USE_SSL false,
URL_STYLE 'path' -- MinIO 及兼容存储必须设置
);
-- 从 S3 直接查询 Parquet
SELECT count(*) FROM read_parquet('s3://my-bucket/events/2026/*.parquet');
-- 从 S3 查询 CSV
SELECT * FROM read_csv_auto('s3://my-bucket/users.csv') LIMIT 10;
-- 从 S3 查询 JSON
SELECT * FROM read_json_auto('s3://my-bucket/config.json');
-- 写回 Parquet 到 S3(需要桶的写权限)
COPY (SELECT * FROM read_parquet('/tmp/*.parquet') WHERE year = 2026)
TO 's3://output-bucket/filtered/2026.parquet' (FORMAT parquet);
DuckDB v1.5.x — 传统 SET 命令
-- 在 DuckDB 会话中配置 S3 凭据(仅 v1.5.x)
SET s3_endpoint = 'minio.example.com:9000';
SET s3_access_key_id = 'admin';
SET s3_secret_access_key = 'your-secret-key';
SET s3_use_ssl = false; -- https 端点用 true
SET s3_region = 'us-east-1'; -- MinIO 写 'us-east-1' 即可(region 被忽略)
SET s3_url_style = 'path'; -- MinIO 及兼容存储必须设置
连接 pgcli 管理的 S3 存储
pgcli 管理了多种 S3 兼容存储(MinIO、silo、rustfs、garage、seaweedfs)。
DuckDB 直接查询它们——s3_endpoint 就是存储的 HTTP 地址,凭据来自
pg addon password。
先创建 S3 存储实例和桶。MinIO、silo(Pigsty 的 MinIO 分支)和 rustfs
(Rust 重新实现)的设置方式完全相同——它们共用 pg mc 包装命令,DuckDB
端也需要相同的 s3_url_style = 'path':
# MinIO
pg addon install minio --name ms3
S3_SECRET=$(pg addon password minio --name ms3)
# 或 silo(功能面一致,与 minio 共用端口池)
pg addon install silo --name ms3
S3_SECRET=$(pg addon password silo --name ms3)
# 或 rustfs(Rust S3 兼容存储,仅 Linux)
pg addon install rustfs --name ms3
S3_SECRET=$(pg addon password rustfs --name ms3)
# 通过 pg mc 包装命令创建桶
pg mc alias set ms3 http://127.0.0.1:9000 admin "${S3_SECRET}"
pg mc mb ms3/my-bucket
# 上传测试数据(可选)
pg mc cp /tmp/data.parquet ms3/my-bucket/data.parquet
然后从 DuckDB 查询——MinIO、silo、rustfs 及兼容存储需要
URL_STYLE 'path'(不支持 virtual-host style):
# DuckDB v2.0.0+
duckdb -c "
CREATE SECRET (
TYPE S3,
ENDPOINT '127.0.0.1:9000',
KEY_ID 'admin',
SECRET '${S3_SECRET}',
USE_SSL false,
URL_STYLE 'path'
);
SELECT count(*) FROM read_parquet('s3://my-bucket/data/*.parquet');
"
# DuckDB v1.5.x(传统 SET 语法)
duckdb -c "
SET s3_endpoint = '127.0.0.1:9000';
SET s3_access_key_id = 'admin';
SET s3_secret_access_key = '${S3_SECRET}';
SET s3_use_ssl = false;
SET s3_url_style = 'path';
SELECT count(*) FROM read_parquet('s3://my-bucket/data/*.parquet');
"
如需更持久的配置,创建 DuckDB 启动文件(~/.duckdbrc),其中的命令
会在每次会话启动时执行:
-- ~/.duckdbrc (DuckDB v2.0.0+)
CREATE SECRET (
TYPE S3,
ENDPOINT 'minio.example.com:9000',
KEY_ID 'admin',
SECRET 'your-secret-key',
USE_SSL false,
URL_STYLE 'path'
);
S3 环境变量
DuckDB 从 DUCKDB_S3_* 环境变量读取端点和 SSL 设置,但凭据必须使用标准 AWS
变量(AWS_ACCESS_KEY_ID、AWS_SECRET_ACCESS_KEY)——DuckDB 的
enable_global_s3_configuration 会自动检测这些变量。
v2.0.0 限制:URL_STYLE 无法通过环境变量设置。它默认为 virtual-host,
会导致 MinIO/silo/rustfs(需要 path-style)无法访问。对于这些存储,
应使用 CREATE SECRET 并指定 URL_STYLE 'path'。
# 端点和 SSL 通过 DUCKDB_S3_*
export DUCKDB_S3_ENDPOINT='127.0.0.1:9000'
export DUCKDB_S3_USE_SSL='false'
# 凭据通过 AWS_*(标准变量,DuckDB 自动检测)
export AWS_ACCESS_KEY_ID='admin'
export AWS_SECRET_ACCESS_KEY='your-secret-key'
# URL 样式仍需在会话或启动文件中设置(v1.5.x)
duckdb -c "SET s3_url_style = 'path'; SELECT * FROM read_parquet('s3://my-bucket/data.parquet');"
# v2.0.0 中 URL_STYLE 必须通过 CREATE SECRET 设置——环境变量无法覆盖
# MinIO/silo/rustfs 不支持 virtual-host style,必须显式指定 path
duckdb -c "
CREATE SECRET (TYPE S3, ENDPOINT '127.0.0.1:9000', KEY_ID 'admin',
SECRET 'your-secret-key', USE_SSL false, URL_STYLE 'path');
SELECT * FROM read_parquet('s3://my-bucket/data.parquet');
"
对于 MinIO/silo/rustfs,仍需设置 s3_url_style = 'path',因为这些存储
不支持虚拟主机样式 URL。