DuckDB 和 MinIO 用于现代数据栈

DuckDB and MinIO for a Modern Data Stack

现代数据栈是一组用于处理当今世界数据的工具,但其精确定义存在争议。更简单地说,它不是过去大型软件公司青睐的垂直扩展整体式方法。相反,该栈由特定、高质量的工具组成,每个工具擅长处理数据的某个特定方面。组件的特殊性和模块化是现代数据栈经常出现变化的原因——随着技术和需求的变化,解决方案总是在不断地加入和退出。尽管不断变化,但该栈通常包括用于集成、转换、可视化和分析数据的工具。

在过去,数据栈应用程序的设计目的是吞噬数据并将数据锁定在它们的生态系统中,但那些日子已经过去了,如今的数据栈,无论其组成部分如何,都通过操作数据所在的位置来运行,将控制权和访问权交回用户手中。现代数据栈应用程序必须能够处理任何地方的数据,无论是在公共云、私有云、裸机基础设施还是边缘计算环境中。软件公司为了留存而创造的孤立数据孤岛已经成为过去。

在这个模型中,DuckDB 有其立足之地,它是一个伪装成开源 OLAP 数据库的分析引擎。DuckDB 为分析师、工程师和数据科学家提供了一个机会,让他们能够快速提取有意义的见解,而无需进行冗长或复杂的提取和加载步骤。他们可以在自己的数据上进行企业级分析和数据探索,而无需移动数据。

MinIO 和 DuckDB

越来越多的企业使用 MinIO 作为数据库的主要存储,例如 DuckDB。它们之间的协同作用使它们成为现代数据栈的理想组合。

  • 性能: 在数据密集型工作负载中,顶级性能是不可谈判的。MinIO 以最快的可用的对象存储解决方案设定了标准,确保数据访问和检索速度极快。与 DuckDB 集成后,这种性能提升可以提高数据分析和处理的效率。
  • 可扩展性: MinIO 通过服务器池实现的可扩展性与 DuckDB 的增长能力完美匹配。随着数据需求的增长,MinIO 无缝扩展的能力确保存储基础设施能够轻松满足这些不断变化的需求。
  • 专为 OLAP 工作负载设计: DuckDB 专为联机分析处理 (OLAP) 工作负载而构建,使其成为分析任务的理想选择。与 MinIO 的存储能力相结合,它为高效管理和分析大型数据集奠定了坚实的基础。
  • 简单性: MinIO 和 DuckDB 都遵循简单性原则。DuckDB 采用轻量级、无依赖性、单文件构建,易于设置。MinIO 直观的软件优先设计补充了这种简单性。
  • 控制: 利用 DuckDB 查询 MinIO 中的数据,而无需移动数据,这意味着你可以完全控制数据。数据永远不会离开你IAM 安全且加密的 MinIO 存储,也不会被锁定在专有基础设施中。
  • 灵活: 你可以在任何有 C++11 编译器的环境中部署 DuckDB。MinIO 可以部署到公共云、私有云、裸机基础设施或边缘计算环境中。有了这些工具,你永远不会被绑定在某个环境中。
  • 现代: 现代数据栈是云原生的。MinIO 从一开始就在云中诞生,旨在从一开始就在裸机和 Kubernetes 上运行。你的数据无处不在——你的分析引擎难道不应该也这样吗?

DuckDB 概述

使 DuckDB 与众不同的是它卓越的速度和多功能性,使用户能够快速处理和分析大型数据集。这种速度是 DuckDB 的列式向量化查询执行引擎的结果。向量化处理与过去查询处理模型有很大不同,这种变化带来的结果是显著提高了查询性能

来自 Mühleisen, Hannes 的图表。 “DuckDB 一个嵌入式分析数据库” FOSDEM。 2020. 

查询处理模型概述

  • 逐元组: 由 PostgreSQL、Oracle 和 MySQL 等数据库使用。逐元组处理意味着一次处理一行数据。
  • 逐列: Pandas 使用此模型,它比逐元组处理速度更快,但在数据大小超过内存时会成为问题。
  • 逐向量: DuckDB 使用此模型,这种方法找到了其他两种模型之间的平衡。查询以数据批处理方式处理,数据批处理由向量集合组成,每个向量包含来自列的固定数量的值。此模型将将来自多个列的值合并成元组(或行)的物化推迟到查询计划的最后。结果是高效地使用 in-cache 操作,通过在尽可能多的查询中将数据保留在非常快的 L1 和 L2 缓存中(参见下图)。
来自 Mühleisen, Hannes 的图表。 “DuckDB 一个嵌入式分析数据库” FOSDEM。 2020。

DuckDB 的多功能性体现在其特性中。对于一个专为速度而构建的数据库,它还提供对复杂查询和函数的意外全面支持。它不仅速度快,而且深度也很深。

限制

当你前往赛道时,你会带一辆高性能汽车。你不会带你的家庭友好型 SUV,尽管它很通用。虽然我们可能通常不会将鸭子视为高性能,但这正是 DuckDB 的用途——对大型数据集进行快速查询。它是数据库中的赛车——你无法在里面放任何东西,它只能容纳两个人,你的高尔夫球杆可能甚至无法放在后备箱里,但它非常适合赛道。它缺少的功能并不影响其优势。这种极简主义是现代数据栈容器化世界中的一个核心设计原则。

然而,了解 DuckDB 的局限性至关重要。DuckDB 不是为事务工作负载而设计的,因此它不适合高并发写入场景。DuckDB 提供了两种并发选项:一个进程可以同时读写数据库,或者多个进程可以同时只读。

另一个需要考虑的方面是缺乏用户管理功能,这使得 DuckDB 更适合个人,而不是需要用户访问控制和权限的协作团队环境。

尽管有这些限制,但只要符合其优势和预期用途,DuckDB 在分析任务中依然表现出色。

MotherDuck: 将 DuckDB 推向新的高度

MotherDuck 是一项针对 DuckDB 的托管服务,可以为你生产化 DuckDB。它将 DuckDB 从单机安装扩展到云端,本质上将其转变为多人游戏体验。通过使用 MotherDuck,你可以进一步扩展 DuckDB,而无需放弃对数据的控制。话虽如此,没有理由不从生产角度考虑 DuckDB。MotherDuck 建立在 DuckDB 的生产就绪核心优势之上,即速度和高效的数据处理。重要的是,这里描述的所有内容也应该在使用 MotherDuck 时无缝地适用。

安装 DuckDB

安装说明可以在DuckDB 的文档中找到。其他人记录了如何将 DuckDB 容器化,有些人将其与 dbt 和 Python 等有用的工具捆绑在一起。是否使用这些扩展取决于你的 SLA 和偏好。这些映像和其他映像都在Docker Hub 上。

对于本地部署,例如用于本教程或开发和测试,请下载适合你操作系统的二进制文件。MacOS 用户可以使用 Homebrew 包管理器将 DuckDB CLI 添加到你的 PATH 中。

无论你如何安装,都通过调用 duckdb CLI 命令来启动 DuckDB。

$ duckdb
v0.8.1 6536a77232
Enter ".help" for usage hints.
Connected to a transient in-memory database.
Use ".open FILENAME" to reopen on a persistent database.

安装 httpfs 扩展,它将允许你从 MinIO 读取数据。

INSTALL httpfs;
LOAD httpfs;

安装 MinIO

在本教程中,我将使用 Docker 部署单节点单驱动器 MinIO 服务器。单节点单驱动器适用于早期开发、评估或学习和教学,如本教程。有关以这种方式部署的说明,请参阅此处此处

要启动一个无根 Docker 容器,请运行以下命令。更改密码始终是最佳实践。

mkdir -p ${HOME}/minio/data

docker run \
   -p 9000:9000 \
   -p 9090:9090 \
   --user $(id -u):$(id -g) \
   --name minio1 \
   -e "MINIO_ROOT_USER=ROOTUSER" \
   -e "MINIO_ROOT_PASSWORD=CHANGEME123" \
   -v ${HOME}/minio/data:/data \
   quay.io/minio/minio server /data --console-address ":9090"

要为生产设置 MinIO,请参阅部署 MinIO:多节点多驱动器指南。当你导航到 MinIO 控制台时,系统会提示你创建存储桶。

为你的教程创建一个易于记忆且描述性的存储桶名称。我将我的命名为:bookings。查看下方图像中工具提示中包含的存储桶命名规则。

数据集

本教程的数据集 是两家酒店的预订信息。下载数据集,然后将文件加载到 MinIO 中。你可以使用 MinIO 控制台或MinIO 客户端,mc。

将所有内容整合在一起

在启动 DuckDB 的命令行窗口中,你可以使用 DuckDB CLI 与数据库交互。使用你的 MinIO 用户名和密码作为你的 S3 访问密钥和 S3 秘密密钥。如果任何人可以访问你的机器以对其进行攻击,你应该更改这些值。MinIO 忽略区域,但你可以在需要时将区域设置为“us-east-1”,如本例所示。从你的端点中删除“http”,并记住你的端口号应该是 MinIO S3-API 的端口号,而不是控制台的端口号。非常关键的一点是,确保你的 s3_url_style 设置为“path”。

INSTALL httpfs;
LOAD httpfs;
SET s3_region='us-east-1';
SET s3_url_style='path';
SET s3_endpoint='play.min.io:9000';
SET s3_access_key_id='***' ;
SET s3_secret_access_key='***';

有了这些设置,你就可以开始查询数据了。首先使用 bookings 数据创建一个表。这是另一个很棒的 DuckDB 功能,为了创建表,你无需花费时间定义你的模式。

CREATE TABLE bookings AS SELECT * FROM read_csv_auto('s3://bookings/hotel_bookings.csv', all_varchar=1);

首先查询 MinIO 中的数据,以了解其大小和结构。

SELECT COUNT(*) AS TotalRows from bookings;
┌───────────┐
│ TotalRows │
│   int64   │
├───────────┤
│    119390 │
└───────────┘
SELECT column_name FROM information_schema.columns WHERE table_name = 'bookings';
┌────────────────────────────────┐
│          column_name           │
│            varchar             │
├────────────────────────────────┤
│ hotel                          │
│ is_canceled                    │
│ lead_time                      │
│ arrival_date_year              │
│ arrival_date_month             │
│ arrival_date_week_number       │
│ arrival_date_day_of_month      │
│ stays_in_weekend_nights        │
│ stays_in_week_nights           │
│ adults                         │
│ children                       │
│ babies                         │
│ meal                           │
│ country                        │
│ market_segment                 │
│ distribution_channel           │
│ is_repeated_guest              │
│ previous_cancellations         │
│ previous_bookings_not_canceled │
│ reserved_room_type             │
│ assigned_room_type             │
│ booking_changes                │
│ deposit_type                   │
│ agent                          │
│ company                        │
│ days_in_waiting_list           │
│ customer_type                  │
│ adr                            │
│ required_car_parking_spaces    │
│ total_of_special_requests      │
│ reservation_status             │
│ reservation_status_date        │
├────────────────────────────────┤
│            32 rows             │
└────────────────────────────────
SELECT DISTINCT(market_segment) FROM bookings;
┌────────────────┐
│ market_segment │
│    varchar     │
├────────────────┤
│ Direct         │
│ Corporate      │
│ Online TA      │
│ Offline TA/TO  │
│ Complementary  │
│ Groups         │
│ Undefined      │
│ Aviation       │
└────────────────┘

使用 Python 扩展和操作化你的分析

DuckDB 官方 API 封装 包括 C、Python、R、Java、Node.js、WebAssembly/Wasm、ODBC API、Julia 和 CLI,我们之前已经介绍过。

这个简短的 Python 脚本将帮助您开始在 CLI 之外构建一些关键的数据探索指标。我已将文件格式切换为 Parquet,以展示更多关键的 DuckDB 功能。

您可以在我们的 Github 仓库中找到这个脚本 这里

我的 Parquet 文件中的数据如下所示

以下是脚本的输出

Number of rows in data: 4
Number of nulls in 'Insect': 0
Number of nulls in 'Order_of_Insects': 0
Number of nulls in 'Habitat': 0
Number of nulls in 'Predatory': 0
Number of nulls in 'Units': 0
Summary statistics for 'Units':
  Min: 1
  Max: 9
  Average: 4.5
  Standard Deviation: 3.415650255319866

不要成为一只坐以待毙的鸭子

这篇博文向您展示了如何配置 DuckDB 从 MinIO 读取数据,然后运行一些探索性查询。DuckDB 是一个用于快速数据分析的出色小工具。您可以用 DuckDB 和 MinIO 做更多的事情。例如,DuckDB 可以与 对象 Lambda 一起使用,以自动对 MinIO 中的数据执行 SQL 查询。例如,可以将清理脚本的执行设置为新原始数据进入 MinIO 存储桶的事件通知。MinIO 和 DuckDB 之间的企业集成可能性是无限的。

当您开始使用 DuckDB 和 MinIO 时,欢迎您加入我们的 Slack 频道。我们很乐意了解您的使用体验,如果您有任何问题,请随时与我们联系。