很高兴您拿起这本书,并准备学习一种似乎与我们过去十年所学的大数据系统知识背道而驰的技术。我们使用 DuckDB 的过程中收获了很多乐趣,希望您读完本书后也会和我们一样兴奋。本书将采用实践动手、简洁明了、节奏紧凑的方式进行讲解,并穿插大量代码示例。
学完本书后,您将能够使用 DuckDB 分析各种格式的表格数据。同时,您的数据处理工具箱中也将多出一个用于数据转换、清理和格式转换的得力助手。在性能不足以使用 pandas DataFrames 的场景中,您可以将 DuckDB 集成到 Python 笔记本和程序中进行替换。利用 Streamlit 和 DuckDB,您还可以快速构建数据分析应用程序。
让我们开始吧!
什么是DuckDB?
DuckDB 是一个现代化的嵌入式分析数据库,它运行在您的机器上,可以让您高效地处理和查询来自不同来源的的海量数据。
DuckDB 的诞生:
DuckDB 是一个于 2018 年由当时荷兰国家数学和计算机科学研究机构 Centrum Wiskunde & Informatica (CWI) 的数据库系统研究人员 Mark Raasveldt 和 Hannes Mühleisen 及其导师 Peter Boncz 创建的现代化嵌入式分析数据库。 创始人和 CWI 联合成立了 DuckDB Labs 公司,致力于进一步开发 DuckDB。其工程团队专注于提高 DuckDB 的效率、用户友好性以及集成度。
管理与维护:
非盈利组织 DuckDB 基金会通过保护知识产权并确保开源项目在 MIT 许可证下持续发展来管理 DuckDB 项目。基金会运营和 DuckDB 的开发由商业会员资助,而关联会员可以参与制定开发路线图。
DuckDB 生态系统:
虽然 DuckDB 专注于本地数据处理,但另一家初创公司 MotherDuck 旨在将 DuckDB 扩展为一个分布式的自助式分析系统,可以处理云端和边缘计算中的数据。它为 DuckDB 添加了协作和共享功能,并支持处理来自各种云存储的数据。 DuckDB 的生态系统非常广泛,许多个人和组织对它的可能性感到兴奋,并创建了集成和易于使用的应用程序。
社区与支持:
DuckDB 社区非常乐于助人和友好,您可以在 Discord 和 GitHub 上找到他们。 DuckDB 的文档内容全面且详细,足以回答大多数问题。
数据处理能力:
DuckDB 允许您处理和连接不同格式的本地或远程文件,包括 CSV、JSON、Parquet 和 Arrow,以及 MySQL、SQLite 和 Postgres 等数据库。您甚至可以在 Python 脚本或 Jupyter 笔记本中查询 pandas 或 Polars 数据框。有关 DuckDB 典型用法示意图请参见图 1.1。
与 pandas 和 Polars 库不同,DuckDB 是一个真正用于分析的数据库,它实现了高效的数据处理机制,可以在几秒钟内处理海量数据。借助其 SQL 方言,即使是复杂的查询也可以更简洁地表达。它允许您在数据库内部处理更多操作,避免代价高昂的客户端往返通信。
DuckDB 内核数据库引擎的架构是高效处理和内存管理的基础。有关查询处理方式的示意图请参见图 1.2。
我们可以看到,DuckDB 处理查询的方式与其他数据库类似,都包含 SQL 解析器、查询执行计划程序和查询运行时。DuckDB 的查询引擎是矢量化的,这意味着它可以并行处理数据块,并从现代多核 CPU 架构中获益。DuckDB 支持多种扩展,以及用户自定义函数,并提供多种用户界面,例如 CLI、API 或与其他系统进行更底层的集成。
为什么您应该关注 DuckDB?
DuckDB 让数据分析再次变得快速有趣,无需搭建庞大的 Apache Spark 集群或运行云数据仓库来处理仅仅几百 GB 的数据。直接访问来自许多不同来源的数据,并在数据所在位置运行处理(无需通过网络传输),让您的工作更快、更简单、更省钱。这不仅节省了时间,还节省了大量资金,并减少了挫败感。
例如,我们最近需要处理驻留在 S3 中的 AWS 访问日志文件。通常,我们会对压缩的 JSON 文件运行 AWS Athena SQL 查询。这往往会变得昂贵,因为成本的最大部分取决于分析服务扫描的数据量。现在,我们可以将 DuckDB 部署到 EC2 虚拟机上,并以极低的成本查询正在处理的文件。
使用 DuckDB,您可以快速地在本地运行大量实验并验证您的想法和假设,所有这些都只需使用 SQL 即可完成。除了支持 ANSI SQL 标准之外,DuckDB 的 SQL 方言还包括一些创新功能,例如:
- 使用
SELECT * EXCLUDE()
和SELECT * REPLACE()
简化SELECT *
查询 - 通过所有列进行排序和分组,例如
GROUP BY ALL
,可以节省用户键入所有字段名称的麻烦 - 使用
PIVOT
和UNPIVOT
转置行和列 - DuckDB 提供了一种名为
STRUCT
的数据类型及其相关函数,可以轻松处理复杂的嵌套数据。
我们如此热衷于 DuckDB,是因为它可以简化数据管道和数据准备工作,从而为您节省更多时间进行实际的分析、探索和实验。在这本书中,我们希望说服您以下几点:
- DuckDB 在分析工作负载方面优于 SQLite。
- DuckDB 比 Spark 集群更易于设置。
- DuckDB 比 pandas 内存占用更少。
- DuckDB 不像 Polars 那样抛出奇怪的 Rust 错误。
- DuckDB 比 Postgres、Redshift 和其他关系数据库更易于设置和使用。
- DuckDB 在数据转换方面比 Talend 更快速、更强大。
您应该在什么时候使用 DuckDB?
DuckDB 适用于所有可以用 SQL 表达的分析任务,并且处理结构化数据(例如表格或文档),前提是您的数据是预先存在而非流式传输的,并且数据量不超过数百 GB。DuckDB 可以处理之前提到的各种数据格式,并且可以扩展集成到其他系统中。
由于数据不会离开您的系统(本地或隐私保护托管),DuckDB 也非常适合分析隐私相关数据,例如健康信息、家庭自动化数据、患者数据、个人身份信息、财务报表等数据集。
以下是 DuckDB 擅长解决的一些常见分析任务示例:
- 直接分析日志文件所在位置,无需将它们复制到新位置。
- 个人进行自我健康数据量化,例如跑步者监控心率时可能会这样做。
- 使用智能电表的电力生成和消耗报告。
- 优化现代交通运营中的自行车和汽车骑行数据。
- 机器学习训练的用户生成数据的预处理和清理。
DuckDB 的一个重要用途是更有效地处理已经存在于 pandas 或 Polars 数据框中的数据,因为它可以直接访问进程中的数据,而无需从数据框的内存表示中复制数据。
DuckDB 生成的输出和表也适用如此。它们可以直接用作数据框,而无需额外的内存使用或传输。
何时不应使用 DuckDB?
DuckDB 虽然强大,但也有其适用范围的限制,以下是一些不适合使用 DuckDB 的场景:
1. 频繁写入和事务处理
由于 DuckDB 是一个分析型数据库,它对事务和并行写访问的支持非常有限。 因此,您无法将其用于处理和存储随时到达的输入数据的应用程序和 API 中。 同样,如果有多个并发进程从可写数据库中读取数据,也不适合使用 DuckDB。
2. 处理超大规模数据
DuckDB 可处理的数据量主要受限于计算机的主内存大小。 虽然它支持将数据溢出到磁盘(内存溢出处理),但此功能更适用于特殊情况,例如最后几 percent 的处理无法放入内存中。 在大多数情况下,这意味着您的处理限制在数百 GB 左右,并非所有数据都需要同时位于内存中,因为 DuckDB 会优化仅加载所需内容。
3. 复杂企业环境
DuckDB 专注于满足各种数据分析用例的长期需求。 因此,如果您处于具有复杂设置的企业环境中,需要处理数 TB 的数据,那么 DuckDB 可能就不是您的最佳选择。
4. 实时数据流处理
DuckDB 不支持处理不断更新的实时数据流。 数据更新应该通过一次批量加载新表或大量新数据来完成。 DuckDB 不是一个流式实时数据库,您需要自己实现批处理方法,例如设置一个进程,从流中创建迷你批次的数据并将其存储在某个位置,以便 DuckDB 可以对其进行查询。
DuckDB 的使用案例
DuckDB 作为一个数据分析工具,拥有广泛的应用场景。当然,最令人兴奋的是可以将它集成到现有的云、移动、桌面和命令行应用程序中,在后台默默完成任务。在这些场景下,DuckDB 将会像 SQLite 一样被广泛使用,但它用于分析处理而不是事务性数据存储。
当分析用户设备上的数据(例如健康、训练、财务或家庭自动化数据)时,高效的本地基础设施就派上用场了。本地分析和预处理还可以减少需要从边缘设备(例如智能电表或传感器)传输的数据量。
DuckDB 也适用于快速分析大型数据集(例如日志文件),可以在数据存储的位置进行计算和缩减,从而节省大量数据传输时间和成本。目前,云厂商提供像 BigQuery、Redshift 和 Athena 这样的昂贵分析服务来处理此类数据。未来,您可以使用计划的云函数来处理 DuckDB 中的数据,替代许多此类用途。您还可以通过将中间结果写入云存储来链接这些处理函数,然后还可以用于审计目的。
对于数据科学家来说,DuckDB 可以比 pandas 或其他 DataFrame 库更有效地进行数据准备、分析、过滤和聚合,这得益于 DuckDB 最先进的查询引擎。所有这些操作都可以在舒适的 Jupyter Notebook 或使用 Python/R API 的环境中完成,无需离开熟悉的界面。这将使数据科学家能够更轻松地利用大型数据集进行高级数据分析,从而提高工作效率。本书稍后将展示一些相关的案例。此外,DuckDB 可以大大降低设置的复杂性,无需数据运维团队的参与。
最后一种令人兴奋的用例是云存储、边缘网络和本地设备之间的数据分布式分析。例如,MotherDuck 项目正在研究这项功能,它允许您将 DuckDB 在云端和本地结合使用进行数据分析。
DuckDB 的适用之处
这本书假设您已经拥有想要分析或转换的一些数据。这些数据可以驻留在平面文件中(例如 CSV、Parquet 或 JSON),也可以位于其他数据库系统(例如 Postgres 或 SQLite)中。
根据您的用例,您可以临时使用 DuckDB 来转换、过滤数据并将其传递到另一种格式。然而在大多数情况下,您会为数据创建表以使其持久化,以便后续进行高效分析。在此过程中,您还可以转换和纠正列名、数据类型和值。如果您的输入数据是嵌套文档,可以将其展开并扁平化,以使关系数据分析更加轻松高效。
这本书假定您已经拥有想要分析或转换的数据。下面将介绍使用 DuckDB 进行数据处理的一般流程:
-
数据准备
- 确定数据来源:您的数据可以是平面文件(例如 CSV、Parquet 或 JSON),也可以是其他数据库系统(例如 Postgres 或 SQLite)中的数据。
- 选择数据处理方式:您可以临时使用 DuckDB 来转换、过滤数据并将其传递到另一种格式。在大多数情况下,您会创建持久化数据表,以便后续进行高效分析。在此过程中,您还可以转换和纠正列名、数据类型和值。如果您的输入数据是嵌套文档,可以将其展开并扁平化,以使关系数据分析更加轻松高效。
-
数据探索
- 确定分析需求:您需要弄清楚哪些 SQL 功能或 DuckDB 特性可以帮助您进行分析或转换。
- 进行探索性数据分析 (EDA):快速获取数据分布、范围和关系的概览。
-
数据分析
- 构建 SQL 语句:逐步构建相关的 SQL 语句,并逐个步骤验证生成的样本结果是否符合您的预期。
- 创建辅助表或视图:在此阶段,您可能会创建额外的表或视图,以方便后续使用高级 SQL 功能(例如窗口函数、公共表表达式和透视表)。
-
结果输出
- 确定结果去向:决定如何使用分析结果,例如将它们转换为文件或数据库,通过应用程序或 API 提供给用户,或者在 Jupyter notebook 或仪表板中进行可视化呈现。
DuckDB 的数据处理流程步骤
以下部分将概述 DuckDB 架构和功能集的一些特定方面,旨在帮助您全面理解和掌握 DuckDB 的使用。我们将按照使用 DuckDB 的顺序来组织这些章节,从加载数据到填充表格、编写分析用 SQL 语句再到结果可视化,如图 1.4 所示。
数据格式和来源
DuckDB 支持多种数据格式和数据源,让您可以轻松检查和分析数据。与其他数据系统(例如 SQL Server)不同,您无需预先指定架构细节。当读取数据时,数据库会使用合理的默认值和数据中固有的架构信息,您也可以根据需要进行覆盖。
DuckDB 支持多种数据格式,让您可以轻松加载和分析数据:
- CSV 文件: 可以批量并行加载 CSV 文件,并自动映射其列。
- DataFrames 内存: DuckDB 可以直接处理同一个 Python 进程中的 DataFrame 内存,无需复制数据。
- JSON/JSONLines 格式: DuckDB 可以将 JSON 或 JSONLines 格式的数据解构、扁平化并转换为关系型表格。DuckDB 还提供 JSON 数据类型用于存储此类数据。
- Parquet 文件: DuckDB 可以查询 Parquet 文件及其架构元数据。查询中使用的谓词会下推到 Parquet 存储层进行计算,以减少加载的数据量。Parquet 是数据湖泊理想的列式格式,可用于读写数据。
- Apache Arrow 列式数据: DuckDB 可以通过 Arrow 数据库连接 (ADBC) 访问 Apache Arrow 列式数据,无需复制和转换数据。
- 云存储: DuckDB 可以访问云存储桶(例如 S3 或 GCP)中的数据,减少传输和复制基础设施,并允许廉价处理大量数据。
DuckDB 的数据结构
DuckDB 支持各种表格、视图和数据类型。对于表格列、处理过程和结果,DuckDB 提供了比传统数据类型更多的选择,例如字符串 (varchar)、数值 (整数、浮点数、十进制数)、日期、时间戳、间隔、布尔值和 BLOB (大二进制对象)。
DuckDB 还支持结构化数据类型,例如枚举、列表、映射 (字典) 和结构体。
- 枚举 (enum) 是集合中经过索引的命名元素,可以高效存储和处理。
- 列表 (list) 或数组 (array) 可以容纳多个相同类型的元素,并且提供各种操作列表的函数。
- 映射 (map) 是高效的键值对,用于保存键控数据点。它们在 JSON 处理过程中使用,可以通过多种方式构建和访问。
- 结构体 (struct) 是一致的键值结构,其中相同的键始终具有相同数据类型的的值。这使得结构体的存储、推理和处理更加高效。
DuckDB 还允许您创建自己的类型,扩展功能也可以提供额外的的数据类型。DuckDB 可以通过表达式创建虚拟列或派生列,这些列由其他数据派生而来。
开发 SQL 查询
在分析数据时,您通常会从理解数据结构开始。然后,您可以从简单的查询逐步创建更复杂的查询。
您可以使用 DESCRIBE
命令来了解数据源、表格和视图的列和数据类型。掌握这些信息后,您可以通过运行计数查询 (count(*)
) 在全局范围内或按时间、位置或项目类型等维度进行分组,来获取数据集的基本统计信息和分布。这可以让您初步了解可用数据的一些情况。
DuckDB 甚至还提供了一个 SUMMARIZE
子句,可以为您提供按列的统计信息:
count
min
,max
,avg
,std
(标准差)approx_unique
(估计的 distinct 值计数)percentiles
(百分位数 q25、q50、q75)null_percentage
(数据中为 null 的部分)
为了编写分析查询,您可以先使用 LIMIT
子句处理数据子集,或者只查看单个输入文件。首先概述您需要的结果列(这些列有时可能会进行转换,例如使用 strftime
格式化日期)。这些是您需要按其分组的列。然后根据需要对数据应用聚合和过滤器。DuckDB 提供了许多不同的聚合函数,从传统的 min
, avg
, sum
到更高级的函数,例如 histogram
, bitstring_agg
, list
或近似值函数(例如 approx_count_distinct
)。还有一些高级聚合函数,包括百分位数、熵或回归计算以及偏度。对于运行总计以及与前后行的比较,您可以使用窗口函数聚合 OVER (PARTITION BY column ORDER BY column2 [RANGE ... ])
。分析语句中重复使用的一部分可以提取成命名的公共表表达式 (CTE) 或视图。为了提高可读性,还可以将计算的一部分移动到子查询中,并使用其结果来检查是否存在或进行一些嵌套数据准备。
在构建分析语句时,您可以随时检查结果以确保它们仍然正确,并且您没有走错路。这将引导我们进入有关使用查询结果的下一个也是最后一个部分。
使用或处理结果
您已经编写了语句并从 DuckDB 快速获得了分析结果。现在该做什么呢?
编写完语句并从 DuckDB 快速获得分析结果后,您可能会想知道如何利用这些结果。这里提供了一些常用的方法:
保存结果
将结果保留以便日后使用或与其他数据进行比较会非常有用。DuckDB 提供了多种方式保存结果:
- 创建表格 : 使用
CREATE TABLE <名称> AS SELECT ...
语句可以轻松地将结果创建为一个永久表格。 - 导出文件: DuckDB 支持将结果导出为多种格式的文件,例如 CSV、JSON、Parquet、Excel 和 Arrow。
- 其他数据库格式: 通过自定义扩展,DuckDB 还支持将结果导出为 SQLite、Postgres 等其他数据库格式。
- DuckDB 命令行界面 (CLI) : 对于较小的结果集,您可以使用 DuckDB CLI 将数据直接输出为 CSV 或 JSON 格式。
数据可视化
俗话说"百闻不如一见",可视化的图表通常比阅读原始数据更直观易懂。DuckDB 提供了一些数据可视化选项:
- 内置 bar 函数: 使用此函数可以在终端直接生成数据的条形图。
- 命令行绘图工具: 您可以使用一些命令行绘图工具(例如 youplot)快速生成结果的图表。
高级可视化:
对于更复杂的可视化需求,您可以借助庞大的 Python 和 Javascript 生态系统。具体步骤如下:
- 转换结果为 DataFrame: 将 DuckDB 的查询结果转换为 DataFrame 格式。
- 绘图工具: 使用 Python 的 matplotlib 或 ggplot、R 的 ggplot2 或 Javascript 的 d3、nivo、observable 等库将 DataFrame 转换为各种类型的图表。
图 1.5 形象地展示了这一流程。
得益于 DuckDB 的高效运行,您可以直接将查询结果提供给 API 端点,供您或其他人以后使用,或者将其集成到 Streamlit 等应用程序中进行处理。
只有在以下两种情况下才需要单独设置服务器:
- 源数据太大,移动数据不方便。
- 查询结果相比源数据较小 (结果数据量远小于源数据总量的 1%)。
在其他情况下,您可以直接将 DuckDB 嵌入到您的应用程序中,使其直接处理本地原始数据或本地 DuckDB 数据库。
总结
-
DuckDB 是一个新开发的分析型数据库,擅长内存处理。
-
该数据库支持扩展的 SQL 方言,并可以通过扩展获得新的功能。
-
DuckDB 可以原生读取本地和远程来源的各种格式数据。
-
它可以无缝高效地集成到 Python、R 等语言中。
-
作为一个进程内数据库,它可以高效地处理数据而无需复制。
-
除了传统的數據类型之外,DuckDB 还支持列表、映射、结构体和枚举类型。
-
DuckDB 提供了许多针对数据类型和值的函数,使数据处理和整形更加方便。
-
在了解数据结构后逐步构建 SQL 查询有助于保持控制力。
-
您可以将查询结果用于各种方式,从生成报告和图表可视化到输出为新格式。