Echo's blog
Echo's blog
· 1 min read · 资讯

DuckDB为什么这么快

DuckDB 为什么这么快#

一个跑在笔记本电脑上的单文件数据库,查几亿行数据比集群里的数仓还快。这不是营销话术,是过去几年数据库圈最让人兴奋的真实故事。

DuckDB,2019 年从 CWI 阿姆斯特丹的一个研究项目起步,到今天已经成为嵌入式分析数据库的事实标准。没有服务器,没有守护进程,没有端口。你把它当库加载,就像 import numpy 一样简单。单个二进制不到 20 MB,零外部依赖。但它能做的,是让很多重型数仓都汗颜的事情——扫描百万行、聚合、连接、过滤,一气呵成。

为什么这么快?答案藏在每一层设计里。

嵌入式架构:最快的查询是不用传输的查询#

嵌入式架构

DuckDB 和其他数据库最本质的区别,不在算法,不在存储格式,而在架构。

传统数仓的查询流程是这样的:你的 SQL 从应用发出,经过 ODBC/JDBC 驱动序列化成线协议,通过网络传到远端服务器,服务器解析执行,然后把结果集一行一行序列化回来,再反序列化成应用能用的格式。每一步都有成本,但最致命的是网络。

2017 年 Raasveldt 和 Mühleisen 的一篇论文测量了这个开销的真相:千兆以太网的理论上限大约是 125 MB/s。如果你的查询结果集有几百 MB——这在分析场景里太常见了——光传输时间就比计算时间还长。更不用说序列化和反序列化的 CPU 开销。

DuckDB 直接绕过了这个问题。它跑在你的进程里,数据就在本地。没有网络层,没有序列化,没有协议开销。查询结果从内存到应用用指针传递,而不是用 TCP 包。

后来出现的 ADBC(Arrow Database Connectivity)协议也在解决类似的问题——用列式 Arrow 格式传输,避免逐行序列化,相比 ODBC/JDBC 能做到 10 到 100 倍的提升。但 DuckDB 更进一步:它可以直接零拷贝读取 pandas DataFrame 和 Arrow 数据,连”导入数据”这一步都省了。数据在什么格式,就在什么格式上查。

去掉网络不是优化技巧,是架构选择。这个选择让后续所有优化都能在”本地执行”这个前提下展开。

列式压缩与 ZoneMaps:存储本身就在加速#

列式压缩

分析查询和事务查询有根本不同的访问模式。事务查询说”给我用户 42 的记录”,分析查询说”给我三月份所有用户的平均消费”。后者需要扫描大量行,但只关心少数列。

行存数据库在这类场景下做了大量无用功——把整行数据读上来,只取其中一个字段。DuckDB 用列式存储,每一列连续存放,查询只需要读取涉及的列,IO 量直接除以列数。

但这只是开始。DuckDB 对每一列数据块维护了 ZoneMaps——也就是每个数据块的统计摘要:最小值、最大值、是否有空值。当查询带 WHERE 条件时,DuckDB 可以快速跳过那些肯定不包含目标数据的块。

举个例子:一张表按时间分区存放了一年的销售数据,你查某一天的记录。ZoneMaps 一看某个数据块的时间范围是 6 月到 7 月,跟你的查询条件不匹配,直接跳过整个块,连读都不读。这种”先看摘要再决定要不要读”的策略,让全表扫描这个听起来很笨的操作,在实际中往往只扫描了很小一部分数据。

优化器:1 毫秒内的精妙决策#

毫秒优化器

SQL 是一条声明式语言——你说”我想要什么”,不说”怎么得到”。把”想要什么”翻译成”怎么得到”,是优化器的职责。

DuckDB 的 SQL 处理管线分为四步:

  1. 解析——把 SQL 文本变成抽象语法树,DuckDB 用了 PostgreSQL 解析器的分支
  2. 绑定——解析名字、做类型检查,把裸的语法树变成带语义信息的内部表示
  3. 规划——生成逻辑执行计划
  4. 优化——对计划做一连串小规模的变换,每一步解决一个具体问题

这些变换包括:谓词下推(把过滤条件尽早应用到数据源上)、关联子查询去关联(把子查询改写成 join)、连接顺序优化、基于 ZoneMaps 的分区裁剪。

其中最关键的优化是连接顺序。DuckDB 把查询建模成一张图——每个表是一个节点,每个连接谓词是一条边。算法的目标是找到代价最小的连接顺序,用动态规划或 DPccp 这类算法搜索最优解。为什么这步这么重要?因为不同的连接顺序,执行代价可能差好几个数量级。先连接大表再连接小表,和反过来,内存消耗和执行时间完全不同。

整个优化过程,DuckDB 能在大约 1 毫秒 内完成。这个数字值得反复品味——在你敲下回车到看到结果之间那几乎不可感知的瞬间里,DuckDB 已经完成了语法分析、类型推导、逻辑规划、数十种优化变换,并且找到了全局近似最优的执行路径。

写在后面:这只是冰山一角#

冰山一角

Part 1 覆盖的是 SQL 从文本到优化后的执行计划的完整链路:解析 -> 绑定 -> 规划 -> 优化。你看到的是一个跑在你进程里的数仓,没有网络开销,用列式压缩和 ZoneMaps 在存储层加速,用一套精干的优化器在毫秒级完成查询改写。

但真正让 DuckDB 快得离谱的秘密武器还没有展开——向量化执行引擎。传统的逐行执行模型在分析查询里饱受虚函数调用和分支预测失败之苦,DuckDB 用 SIMD 友好的向量化模型批量处理数据,把 CPU 的每一条指令都用到了极致。后续的 Part 会深入这个主题。

在那之前,下次你在笔记本上跑一个几亿行的聚合查询、看到结果几乎瞬间返回的时候,可以想想这个不到 20 MB 的二进制文件里,装着多少精妙的工程智慧。

来源:Hacker News

Related Posts

Comments

Copied
Copied to clipboard