编程 DuckDB 深度实战:把「分析引擎」塞进应用进程里——向量化执行、列式存储与直查 PB 级 Parquet 湖仓的完整指南(2026)

2026-07-08 12:15:42 +0800 CST views 28

DuckDB 深度实战:把「分析引擎」塞进应用进程里——向量化执行、列式存储与直查 PB 级 Parquet 湖仓的完整指南(2026)

关键词:DuckDB、in-process OLAP、向量化执行、列式存储、Parquet、数据湖仓、ETL、Arrow、pg_duckdb

有人说 DuckDB 是「分析界的 SQLite」。这句话只说对了一半:它确实像 SQLite 一样零依赖、嵌入式、文件即数据库;但它干的事恰恰和 SQLite 相反——SQLite 为「一次查一行」的点查而生,DuckDB 为「一次扫一亿行」的分析而生。本文带你从工程视角彻底搞懂它为什么快、怎么用、以及它到底该放在你架构的哪一层。


一、背景介绍:为什么我们需要一个「嵌入式分析数据库」

1.1 数据的三种体量

做工程的我们都知道,数据规模大致可以切成三档,每一档对应一套完全不同的技术选型:

  • 内存放得下(百 MB 以内):用 Python 的 pandas、R 的 data.frame 就够了,循环也跑得动。
  • 磁盘放得下、单机放得下(GB ~ 数 TB):这是绝大多数公司 80% 的真实业务数据所处的区间——一张几年的订单表、一份完整的用户行为日志、一个季度的风控特征。
  • 单机放不下(PB 级、需要横向扩展):Hadoop/Spark、ClickHouse 集群、Snowflake、StarRocks 这类分布式系统登场。

过去十年的基础设施投资几乎全砸在第三档(分布式)和第一档(内存 DataFrame)上,而中间那一档长期是个尴尬的真空地带:用 pandas 吧,内存爆了;上 Spark 吧,为了分析 200 GB 数据专门养一个 YARN 集群,纯属杀鸡用牛刀,而且迭代慢、成本高、本地根本跑不起来。

DuckDB 出现的核心动机,就是填这个洞:在你本机的进程里,用接近数据库内核的效率,把「单机放得下」的数据分析做到极致。

1.2 SQLite 解决了 OLTP 的「嵌入式」,但 OLAP 的「嵌入式」长期空白

SQLite 是地球上部署最广泛的数据库,它的成功密码就两条:零依赖 + 嵌入式(in-process)。应用直接 link 一个库,没有服务端、没有连接池、没有 DBA,打开一个文件就能用。

但 SQLite 的存储引擎是为 OLTP 点查优化的——B+ 树行存、强调事务与点查延迟。你让它 SELECT sum(amount) FROM orders WHERE dt BETWEEN ... GROUP BY region 扫一亿行,它会很难受:行存意味着为了算一个 SUM 要把整行(包括你不需要的列)都从磁盘读上来。

于是出现了一个奇怪的局面:

  • 嵌入式 OLTP 有 SQLite;
  • 服务端 OLAP 有 ClickHouse / Druid / Doris;
  • 嵌入式 OLAP 长期没有正经选手。

DuckDB(由荷兰 CWI 的 Mark Raasveldt 与 Hannes Mühleisen 主导,2019 年 SIGMOD 论文 "DuckDB: an Embedded Analytical Database" 正式亮相,MIT 协议)就是来补这个位的。它的设计哲学原话搬自官方 why_duckdb:「DuckDB adopts SQLite's ideas of simplicity and embedded operation, but is designed for analytical query workloads.」

1.3 为什么是现在

三件事同时成熟,让 DuckDB 在 2023 年后突然爆发:

  1. 单机内存白菜化:一台普通云主机轻松 64~256 GB 内存,SSD 持久盘轻松上 TB。大量「分析任务」其实根本不需要分布式,只是以前没有趁手的单机引擎。
  2. 开放列式格式成为事实标准:Parquet / ORC 让「数据文件」本身就是列式、自带统计信息、可被高效裁剪。数据库不再需要把数据「导入」自己的私有格式才能分析——直接读文件就行。
  3. Lakehouse 下沉:Iceberg / Delta Lake 把「表」的定义搬到了对象存储(S3 / 本地磁盘)上的 Parquet 文件集合。分析引擎和存储解耦,DuckDB 这种轻量引擎正好能直接当 Lakehouse 的查询层。

1.4 边界:DuckDB 不是来替代谁的

澄清几个常见误解,也正好说明它和本站已发过的选题不冲突:

维度DuckDBClickHousepandasSpark
部署形态库/进程内,无服务端服务端集群内存 DataFrame分布式集群
最佳数据量GB ~ 数 TB(单机)TB ~ PB(可横向)MB ~ 数百 MBGB ~ PB
读外部文件一等公民(Parquet/CSV/JSON/S3)需引擎表需先 load 进内存一等公民
并发写单写多读多写多写
典型用法应用内分析、本地湖仓、ETL实时分析服务探索性分析大规模批处理

一句话:DuckDB 是「单机分析的操作系统」,不是「分布式分析的替代品」。 你不会用它去扛每秒上万次的在线写入;但你会用它替代脚本里那一堆 pandas.read_csv + 半吊子聚合,以及替代「为了查一个 Parquet 还得起一个 Spark session」的笨重流程。


二、核心概念:DuckDB 到底是什么

2.1 In-process:没有服务端,文件就是数据库

DuckDB 以的形式存在。Python 里 pip install duckdb 后:

import duckdb
con = duckdb.connect("analytics.duckdb")   # 打开一个文件数据库;不传参则是纯内存库
con.execute("CREATE TABLE t(id INTEGER, v DOUBLE)")
con.execute("INSERT INTO t VALUES (1, 1.5), (2, 2.5)")
print(con.execute("SELECT sum(v) FROM t").fetchall())
# [(4.0,)]

注意到没有:没有 hostportusernamepool。它和你的 Python 解释器跑在同一个进程、同一个地址空间里,连 IPC 都没有。这意味着:

  • 零序列化开销:数据在 DuckDB 内部和你的应用之间不需要通过网络或 IPC 拷贝。
  • 即开即用:CI 里、Lambda 函数里、Notebook 里、甚至浏览器(Wasm 版)里都能直接跑。
  • 文件即数据库:.duckdb 文件可以 scp 走、可以 commit 进 Git LFS、可以丢进对象存储当快照。

2.2 直接读文件,零导入

这是 DuckDB 最「爽」的特性。不需要 CREATE TABLECOPY,直接用文件名当表名:

-- 直接查一个 Parquet 文件
SELECT region, sum(amount) AS gmv
FROM 's3://bucket/orders/2026-07.parquet'
GROUP BY region
ORDER BY gmv DESC;

甚至支持目录即分区表

-- 目录下按 dt=xxx/ 分区的 Parquet,DuckDB 自动识别分区列
SELECT dt, count(*) 
FROM 's3://bucket/orders/dt=*/part-*.parquet'
GROUP BY dt;

它支持的「外部源」包括:

  • 文件:Parquet、CSV、JSON(含 JSON Lines)、Arrow IPC、Excel;
  • 远程:通过 httpfs 扩展读 S3 / GCS / HTTP 上的文件;
  • 其他数据库:通过扩展读 PostgreSQL(postgres_scanner / pg_duckdb)、MySQL、SQLite;
  • 湖仓表:通过 iceberg / delta 扩展直接读 Iceberg / Delta 表。

2.3 向量化执行 + 列式存储(重点)

这是 DuckDB 性能的根。两个概念要分开理解:

列式存储(存储层):数据按列而不是按行落盘。分析查询通常只碰少数几列,SELECT avg(price) 时磁盘上只读 price 那一列,其他列(用户名、地址、备注)根本不进内存。

向量化执行(执行层):传统 Volcano 执行模型一次在算子间传递一行(tuple),CPU 大部分时间花在函数调用、虚表分发、分支预测失败上。DuckDB 改为一次传递一个 DataChunk——默认 2048 行的列向量批(batch)。一个 SUM 聚合算子收到的是「一整块 double 数组」,内部用紧凑循环(甚至 SIMD)一口气算完,函数调用次数降到 1/2048。

-- 你写的还是普通 SQL,但引擎在底层以 2048 行一批的方式向量化处理
EXPLAIN ANALYZE
SELECT user_id, count(*) AS cnt
FROM 'events.parquet'
WHERE event = 'purchase'
GROUP BY user_id
ORDER BY cnt DESC
LIMIT 10;

工程直觉:向量化 + 列式,本质上是在用「批处理 + 连续内存」讨好现代 CPU 的缓存和流水线。你写的代码没变,但数据在内存里的排布和算子的调用粒度变了,性能差出 1~2 个数量级是常态。

2.4 事务与 ACID(单写多读)

DuckDB 支持事务,BEGIN / COMMIT / ROLLBACK 都能用,写入通过 checkpoint 持久化。并发模型是单写者(single-writer)+ 多读者:同一时刻允许一个进程写、多个进程/连接读。它不适合做高并发在线写入(那是 OLTP 的活),但做「低频写、高频分析读」非常稳。

2.5 类型系统与 1.5 新特性

DuckDB 的类型系统比传统 SQL 数据库现代得多:原生支持 LISTSTRUCTMAP、嵌套类型、ENUMUUIDTIMESTAMP 全家桶,以及 1.5「Variegata」带来的两个重磅新类型:

  • VARIANT:半结构化数据的统一容器,可以塞任意 JSON 风格的值,查询时再按需抽取,比老的 JSON 类型在性能与易用性上更优。
  • GEOMETRY:内置空间类型,配合 spatial 扩展做点/线/面运算,无需再外挂 PostGIS 才能玩 GIS。

示例:

-- 1.5 的 VARIANT:直接吃进任意 JSON,查询时抽取
CREATE TABLE logs(payload VARIANT);
INSERT INTO logs VALUES ('{"user": "alice", "score": 92, "tags": ["vip","cn"]}');

SELECT
  payload.user,
  payload.score,
  payload.tags[1]
FROM logs
WHERE payload.score > 90;

三、架构分析:一条 SQL 在 DuckDB 里走了哪几步

理解架构,才能在出问题时知道去哪调。DuckDB 的查询路径和主流分析型数据库一致,分四段:

SQL 文本
  │
  ▼
Parser(词法/语法)──► 抽象语法树 AST
  │
  ▼
Binder(绑定/语义检查)──► 把表名、列名、函数解析成内部表示,做类型推导
  │
  ▼
Optimizer(优化器)
  │   ├─ 基于规则(RBO):谓词下推、列裁剪、表达式重写、子查询扁平化
  │   └─ 基于代价(CBO):根据统计信息选 join 顺序、join 算法
  ▼
Execution Engine(向量化执行引擎)
  │   └─ 以 Pipeline + DataChunk 批量处理,多线程并行
  ▼
存储层(列存块 + 压缩 + 统计信息 + 外部文件扫描)

3.1 优化器:把「能不下推的别下推」做到极致

DuckDB 优化器里对你性能影响最大的是这几条规则:

  1. 谓词下推(Predicate Pushdown)WHERE dt = '2026-07-01' 会被尽量推到扫描算子最底层,最好在读 Parquet 之前就用行组的 min/max 统计信息把整块文件/行组过滤掉——文件根本不进内存。
  2. 列裁剪(Projection Pushdown):你只 SELECT price,引擎只读 price 那一列的物理数据。
  3. 分区裁剪(Partition Pruning):目录分区 dt=2026-07-01/ 直接让不匹配的分区目录根本不被打开。
  4. 子查询去关联(Flattening):把 EXISTS / 相关子查询改写成 join,避免嵌套循环。

3.2 执行引擎:向量化 + 多核

如前所述,执行引擎以 DataChunk(2048 行批)为单位在各算子间流动。扫描算子把 Parquet 解码成列向量,过滤、聚合、join 算子都吃列向量、吐列向量。

并行方面,DuckDB 会在扫描流水线两个层面用多线程:

  • 扫描并行:一个大文件按行组(row group)切片,多个线程同时解码不同片段;
  • Pipeline 并行:一条算子链(pipeline)被复制成多份,各自处理不同数据块。

线程数由 threads 参数控制,默认等于 CPU 核心数。在容器里被限制了 CPU 配额时,记得手动设小,否则线程争抢反而更慢。

3.3 存储层:压缩与统计信息是性能的隐藏功臣

DuckDB 对每列按段(segment)存储,并自动选轻量压缩:常量压缩、字典压缩、游程编码(RLE)、位打包(bit-packing)等。更关键的是每段都记录 min/max/null_count 等统计信息(zone map),这是谓词下推能「跳过整块数据」的依据。

对 Parquet 这类外部文件,DuckDB 直接读取文件自带的 footer 统计信息做同样的裁剪,所以「直接查 Parquet」几乎不比「导入后查」慢——这才是它敢喊「零导入」的底气。

3.4 和 SQLite 架构的本质差异

一句话总结差异点:SQLite 的存储与执行是为「点查一行 + 小事务」优化的(行存 B 树、逐行 Volcano);DuckDB 是为「扫全表 + 聚合 + join」优化的(列存、向量化批处理、扫描并行)。同样一句 SELECT sum(x) FROM t,SQLite 要遍历所有行所有列,DuckDB 只读 x 列、按 2048 行一批、多核并行、还能跳过不符合谓词的数据块。


四、代码实战:从「能跑」到「好用」

下面用 Python + SQL 走一遍真实工作流。所有示例都可本地复制运行(DuckDB 1.5)。

4.1 环境准备

# CLI(最推荐,官网下二进制或 brew)
brew install duckdb
duckdb   # 进入交互式

# Python
pip install duckdb

CLI 里几个高频命令:

.read script.sql        -- 执行 SQL 文件
.export json out.json   -- 导出
.tables                 -- 列出表
DESCRIBE tbl;           -- 看 schema

4.2 实战一:零导入直查 Parquet + 分区目录

假设 S3 上有一堆按天分区的订单 Parquet:

import duckdb

# 直接把分区目录当表,dt 自动成为分区列
sql = """
SELECT
    dt,
    region,
    sum(amount)      AS gmv,
    count(*)         AS orders,
    approx_quantile(amount, 0.99) AS p99_amount
FROM read_parquet('s3://my-bucket/orders/dt=*/part-*.parquet',
                  hive_partitioning=true)
WHERE dt BETWEEN '2026-07-01' AND '2026-07-07'
GROUP BY dt, region
ORDER BY dt, gmv DESC;
"""

con = duckdb.connect()
# 配置 S3 访问(用 httpfs 扩展)
con.execute("""
INSTALL httpfs;
LOAD httpfs;
SET s3_region = 'us-east-1';
SET s3_access_key_id = '${ENV:AWS_KEY}';
SET s3_secret_access_key = '${ENV:AWS_SECRET}';
""")
df = con.execute(sql).df()   # 直接出 pandas DataFrame
print(df.head())

read_parquethive_partitioning=true 是神器:目录里的 dt=2026-07-01/ 会被自动解析成 dt 列,无需自己 UNION ALL

4.3 实战二:CSV/JSON 自动推断 schema

-- CSV 自动推断类型、自动探测分隔符
SELECT product, avg(price) AS avg_price
FROM read_csv_auto('sales_*.csv', sample_size=100000)
GROUP BY product;

-- JSON Lines 直接当表
SELECT json_extract_string(payload, '$.user') AS u,
       (payload->>'score')::INT                AS score
FROM read_json_auto('events.jsonl')
WHERE score > 90;

read_csv_auto 会采样前若干行推断列类型、分隔符、是否有表头,省掉手写 schema 的痛苦。sample_size 调大能让类型推断更稳。

4.4 实战三:和 pandas / Polars / Arrow 零拷贝互操作

这是 DuckDB 在 Python 生态里最香的点——它和 Arrow 内存格式打通,和 DataFrame 之间可以零拷贝转换:

import duckdb, pandas as pd

df = pd.DataFrame({"id": [1,2,3], "v": [10,20,30]})

# pandas -> DuckDB(注册为视图,不拷贝数据)
con = duckdb.connect()
con.register("df_view", df)
print(con.execute("SELECT sum(v) FROM df_view").fetchone())   # (60.0,)

# DuckDB 结果 -> pandas
res = con.execute("SELECT id, v*2 AS v2 FROM df_view").df()

# DuckDB 结果 -> Arrow Table(零拷贝)
tbl = con.execute("SELECT * FROM df_view").arrow()

实用建议:不要再用 pandas.read_parquet 把 5 GB 文件整块读进内存再聚合了。让 DuckDB 在文件层面先聚合、只把聚合后的小结果交给 pandas,内存占用和速度都会好一个量级。

4.5 实战四:把 DuckDB 当本地 Lakehouse(CTAS + Iceberg)

CREATE TABLE AS(CTAS)把清洗后的结果落回 Parquet,形成「可复现的数据管道」:

-- 把原始日志清洗成按天分区的宽表
COPY (
  SELECT
    user_id,
    event,
    to_timestamp(ts/1000) AS ts,
    payload
  FROM read_json_auto('raw/events_*.jsonl')
  WHERE event IS NOT NULL
) TO 'warehouse/clean_events/dt=2026-07-01/'
  (FORMAT PARQUET, PARTITION_BY (dt), CODECS ('snappy'));

-- 之后直接查清洗后的湖仓
SELECT event, count(*) FROM 'warehouse/clean_events/dt=*/' GROUP BY event;

接 Iceberg(1.5 起 duckdb-iceberg 能力大幅增强):

INSTALL iceberg;
LOAD iceberg;
-- 直接把 Iceberg 表当普通表查询(catalog 配置略)
SELECT * FROM iceberg_scan('s3://lake/warehouse/db/orders');

4.6 实战五:应用内嵌分析,替代脚本里的 pandas 聚合

一个常见场景:一个 Go/Python 服务需要定期对本地落盘的日志做统计报表。以前要起个 Spark 或写一堆 pandas,现在直接嵌 DuckDB:

# 在 Web 服务里做即席分析(FastAPI 伪代码)
import duckdb

DB = duckdb.connect("app.duckdb", read_only=False)

def daily_report(date: str):
    return DB.execute(f"""
        SELECT region, count(*) AS cnt, sum(amount) AS gmv
        FROM events
        WHERE date = '{date}'
        GROUP BY region
    """).df()

注意 read_only=False 用于写;纯分析只读场景务必用 read_only=True,这样多个进程可以并发打开同一个库文件做读。

4.7 实战六:pg_duckdb / dbt-duckdb 做 ELT

  • pg_duckdb:把 DuckDB 引擎装进 PostgreSQL,让 PG 直接 SELECT ... FROM duckdb.table(...) 查 Parquet/S3,OLTP 和 OLAP 一张嘴。
  • dbt-duckdb:用 dbt 这套成熟的 ELT 编排,把模型跑在 DuckDB 上,本地开发体验和线上一致。
-- pg_duckdb:在 Postgres 里直接分析对象存储上的 Parquet
SELECT u.name, sum(o.amount)
FROM users u
JOIN duckdb.parquet('s3://bucket/orders/*.parquet') o
  ON o.user_id = u.id
GROUP BY u.name;

4.8 实战七:1.5 的空间分析(GEOMETRY)

INSTALL spatial;
LOAD spatial;

CREATE TABLE pois(name VARCHAR, geom GEOMETRY);
INSERT INTO pois VALUES
  ('office', ST_Point(116.39, 39.91)),
  ('shop',   ST_Point(116.40, 39.92));

-- 找出距离 office 1km 内的点(示意)
SELECT a.name, b.name,
       ST_Distance(a.geom::GEOGRAPHY, b.geom::GEOGRAPHY) AS m
FROM pois a, pois b
WHERE a.name='office' AND b.name<>'office';

五、性能优化:把 DuckDB 榨干的正确姿势

DuckDB 开箱已经很快,但用错姿势照样能慢十倍。按性价比排序:

5.1 第一性原理:让「下推」生效

性能优化的 80% 都来自「尽量早地砍掉数据」:

  • 分区列参与过滤:用 dt=... 目录分区,查询里带 dt 条件,触发分区裁剪。
  • 排序列 / 聚簇键:把常过滤的列(如 user_idevent_type)作为 Parquet 的写入排序键,让 min/max 统计更有效。
  • SELECT 需要的列:列裁剪的前提是你别写 SELECT *
  • 文件别太小也别太大:Parquet 单文件 128 MB~1 GB 体验最佳;几 KB 一个的小文件会让元数据开销压垮扫描。
-- 好的写法:分区 + 列裁剪 + 谓词下推全生效
SELECT region, sum(amount)
FROM 'warehouse/orders/dt=2026-07-01/part-*.parquet'
WHERE region = 'cn-east'        -- 分区/列统计可裁剪
GROUP BY region;

-- 差的写法:SELECT * + 在应用层过滤
-- SELECT * FROM 'warehouse/orders/dt=*/part-*.parquet'

5.2 配置:threads / memory_limit

con = duckdb.connect()
con.execute("SET threads = 4")            # 容器里 CPU 受限时手动设
con.execute("SET memory_limit = '4GB'")   # 控制内存上限,避免 OOM
con.execute("SET preserve_insertion_order = false")  # 不需要保序时关掉,聚合更快

memory_limit 设得太小会频繁落盘(spill),设得太大在容器里会被 OOM Killer 干掉。经验值:留 20% 给系统和 Python 本身。

5.3 写入:用 Appender 批量插,别逐行 INSERT

逐行 INSERT 在 DuckDB 里是反模式(每次都要走事务/解析)。大批量写用 Appender 或一次性 COPY / INSERT INTO ... SELECT

con = duckdb.connect("app.duckdb")
app = con.append("events")          # 拿到 Appender
for row in huge_iterator:
    app.append(row)                 # 攒批写,远快于逐行 execute INSERT
app.close()

5.4 中间结果落地:CTAS 比反复子查询稳

长管道里,把中间大结果用 CREATE TABLE ... AS 落盘,比层层嵌套子查询更易调试、可断点续跑,也避免重复计算:

CREATE TABLE step1 AS SELECT ... FROM raw WHERE ...;
CREATE TABLE step2 AS SELECT ... FROM step1 JOIN dim ...;
-- 出问题改 step2 即可,不用重跑整条

5.5 并发读:read_only 模式

多个进程/线程同时分析同一个 .duckdb 文件,全部用 read_only=True 打开即可并发读。一旦有写入者,读者会被锁等待——所以写和分析读尽量错峰,或写方用独立文件、读方读快照。

5.6 看计划:EXPLAIN ANALYZE

一切优化从看执行计划开始:

EXPLAIN ANALYZE
SELECT region, count(*) FROM 'orders/dt=*/part-*.parquet'
WHERE amount > 100 GROUP BY region;

重点看:是否出现了 PARQUET_SCANrows_filtered 很大(说明下推生效);是否某算子耗时占比异常高(可能缺索引/缺分区/数据倾斜)。


六、总结展望:DuckDB 该放在你架构的哪一层

6.1 它擅长什么、不擅长什么

适合:

  • 单机 GB~TB 级数据的即席分析、报表、ETL/ELT;
  • 应用内嵌的分析能力(CLI 工具、数据产品后端、Notebook);
  • 本地 Lakehouse 查询层(直查 Parquet / Iceberg / Delta);
  • 替代「pandas 读大文件再聚合」「为查一个文件起 Spark」的笨重流程;
  • 边缘 / Serverless / CI 里跑分析(无服务端、毫秒启动)。

不适合:

  • 高并发在线写入(OLTP);
  • 需要横向扩展到多机 PB 级、且要求严格 SLA 的实时分析服务(那是 ClickHouse / StarRocks 的活);
  • 多写者强一致的事务系统。

6.2 生态在 2026 年的关键进展

  • MotherDuck:把 DuckDB 搬上云,本地引擎和云端无缝同步,边缘分析 + 云端协作成为现实。
  • pg_duckdb / dbt-duckdb:让 DuckDB 融入 Postgres 与 dbt 生态,ELT 工作流本地就能跑通。
  • Iceberg / Delta 扩展增强:1.5.3 起 duckdb-iceberg 能力大幅增强,DuckDB 正成为「Lakehouse 的本地查询标准入口」。
  • 1.5「Variegata」:全新 CLI、VARIANT 半结构化类型、GEOMETRY 空间类型,把「分析 + 半结构化 + GIS」三件事收进一个引擎。
  • 1.4「Andium」LTS:长期支持版维护到 2026 年 9 月,企业落地有稳定基线。

6.3 给程序员的实在建议

  1. 下次想 pandas.read_parquetgroupby 的时候,先想想这文件有多大。 超过内存 1/3,直接交给 DuckDB,只在最后取聚合结果。
  2. 把你脚本里的「读文件 + 过滤 + 聚合」翻译成一条 SQL。 你会惊讶于代码变短、变快、还更好维护。
  3. 本地用 DuckDB 开发 dbt / Lakehouse 管道,跑通后再上云,开发循环从「分钟级」降到「秒级」。
  4. 别拿它当 MySQL 用。 它快是因为放弃了高并发写和分布式,扬长避短才是工程智慧。

DuckDB 的聪明之处,不在于发明了什么黑科技,而在于把两件早就成熟的事做到了极致:SQLite 式的「零依赖嵌入式」哲学,加上分析型数据库几十年的「列式 + 向量化」积累。它把一个过去要么太重、要么太弱的区间,填成了一条顺滑的本地分析高速公路。

当你下次面对一个「不大不小、上分布式不值当、用 DataFrame 又爆内存」的数据问题时,记得:也许你需要的不是更大的集群,而是一个被你忽略已久的、就躺在进程里的那只鸭子 🦆。


参考资料:DuckDB 官方文档(duckdb.org)why_duckdb、SIGMOD 2019 "DuckDB: an Embedded Analytical Database"、DuckDB 1.5.0 发布说明、duckdb-iceberg / pg_duckdb / dbt-duckdb 项目仓库。本文代码基于 DuckDB 1.5 验证思路,具体 API 以你所装版本文档为准。

推荐文章

基于Flask实现后台权限管理系统
2024-11-19 09:53:09 +0800 CST
php机器学习神经网络库
2024-11-19 09:03:47 +0800 CST
Vue3 结合 Driver.js 实现新手指引
2024-11-18 19:30:14 +0800 CST
HTML + CSS 实现微信钱包界面
2024-11-18 14:59:25 +0800 CST
程序员茄子在线接单