Superset 通过DuckDB连接数据湖


前文介绍了,通过 PostgreSQL + PL/Python 实现数据湖的访问。
今天研究了下,DuckDB 借助 Jinja 模板 也可以轻松实现数据湖的访问。
准备知识
Superset 支持 Jinja 模板
Superset 官方正式添加了 DuckDB 的支持
DuckDB 访问数据湖
什么是 Parquet 文件格式以及为什么要使用它
如果你还没有安装 Superset, 建议看下这篇文章, Apache Superset 2.0 来了。
需要安装好 DuckDB 、 duckdb-engine 还有 deltalake,
pip install duckdb-engine deltalake
代码
superset-config.py 部分
# activate Jinja templatingFEATURE_FLAGS = {   # . . . . .  "ENABLE_TEMPLATE_PROCESSING": True}def delta_table(table_name):    from deltalake import DeltaTable    dt = DeltaTable(table_name)    return dt.file_uris()JINJA_CONTEXT_ADDONS = {    'delta_table': delta_table}
SQL 语句
SELECT * from read_parquet({{delta_table('/Users/steven/data/iris')}})

其中 /Users/steven/data/iris 为 delta lake 表, 做过一次追加
实现代码如下
import pandas as pdiris = pd.read_csv('/Users/steven/data/iris.csv')from deltalake.writer import write_deltalake# 第一次写入write_deltalake( '/Users/steven/data/iris',iris)# 第二次追加write_deltalake( '/Users/steven/data/iris',iris,mode='append')# 查看最新版本parquet 文件列表from deltalake import DeltaTabledt = DeltaTable('/Users/steven/data/iris')dt.file_uris()
返回

最新版本 DuckDB 对分区表的支持
昨天文章写到这的时候,我以为结束了,今天突然想到,如果数据分区了怎么办, 很幸运,最新的 DuckDB 也支持, 如果想体验这个功能,需要安装 0.4.1 版本(当前版本为 duckdb-0.4.1.dev1057)
pip install -U --pre duckdb  -i  https://mirrors.aliyun.com/pypi/simple/#  Attempting uninstall: duckdb#    Found existing installation: duckdb 0.3.2#    Uninstalling duckdb-0.3.2:#      Successfully uninstalled duckdb-0.3.2# Successfully installed duckdb-0.4.1.dev1057
分区测试代码,
# 把iris以Species分区数据写到数据湖write_deltalake( '/Users/steven/data/iris1',iris,partition_by=['Species'])# 读取from deltalake import DeltaTabledt = DeltaTable('/Users/steven/data/iris1')dt.file_uris()# 返回

这个时候 superset 的 SQL lab 里需要这样写了(需要用到 parquet_scan 函数以及 参数 HIVE_PARTITIONING=true),
SELECT * from parquet_scan({{delta_table('/Users/steven/data/iris1')}},HIVE_PARTITIONING=true)

如果不加参数 HIVE_PARTITIONING=true 会丢失分区列

当然即使没有分区,也可以写上这个参数。
附 DuckDB 读取 Parquet
原文 https://duckdb.org/docs/data/parquet
单文件读取
DuckDB 以函数的形式包含一个高效的 Parquet 阅读器 read_parquet。
SELECT * FROM read_parquet('test.parquet');
如果您的文件以 结尾 .parquet,则 read_parquet 语法是可选的。系统会自动推断您正在读取 Parquet 文件。
SELECT * FROM 'test.parquet';
与 CSV 文件不同,Parquet 文件是结构化的,因此阅读起来很明确。无需向此函数传递任何参数。该 read_parquet 函数将找出文件中存在的列名和列类型并发出它们。
多文件读取和全局
DuckDB 还可以读取一系列 Parquet 文件并将它们视为单个表。请注意,这只适用于 Parquet 文件具有相同架构的情况。您可以使用列表参数、glob 模式匹配语法或两者的组合来指定要读取的 Parquet 文件。
列表参数
read_parquet 函数可以接受文件名列表作为输入参数。有关列表的更多详细信息,请参阅嵌套类型文档。
-- read 3 parquet files and treat them as a single tableSELECT * FROM read_parquet(['file1.parquet', 'file2.parquet', 'file3.parquet']);
glob 语法
输入到 read_parquet 函数的任何文件名可以是精确的文件名,也可以使用 glob 语法来读取与模式匹配的多个文件。
通配符 描述
* 匹配任意数量的任意字符(包括无)
? 匹配任何单个字符
[abc] 匹配括号中的一个字符
[a-z] 匹配括号中给定范围内的一个字符

这是一个读取文件夹中所有以结尾的文件的 .parquet 示例 test:
-- read all files that match the glob patternSELECT * FROM read_parquet('test/*.parquet');
glob 列表
glob 语法和列表输入参数可以结合起来扫描满足多种模式之一的文件。
-- Read all parquet files from 2 specific foldersSELECT * FROM read_parquet(['folder1/*.parquet','folder2/*.parquet']);
部分读取
DuckDB 支持将投影下推到 Parquet 文件本身。也就是说,查询 Parquet 文件时,只读取查询所需的列。这允许您只读取您感兴趣的 Parquet 文件的一部分。这将由系统自动完成。
DuckDB 还支持将过滤器下推到 Parquet 阅读器中。当您将过滤器应用于从 Parquet 文件扫描的列时,过滤器将被下推到扫描中,甚至可以使用内置区域图跳过文件的某些部分。请注意,这取决于您的 Parquet 文件是否包含区域图。
过滤器和投影下推提供了显著的性能优势。有关更多信息,请参阅 DuckDB 博客文章[1]。
插入和视图
您还可以将数据插入表中或直接从 parquet 文件创建表。这将从 parquet 文件加载数据并将其插入数据库。
-- insert the data from the parquet file in the tableINSERT INTO people SELECT * FROM read_parquet('test.parquet');-- create a table directly from a parquet fileCREATE TABLE people AS SELECT * FROM read_parquet('test.parquet');
如果您希望将数据保存在 parquet 文件中,但又想直接查询 parquet 文件,您可以在该 read_parquet 函数上创建一个视图。然后,您可以像查询内置表一样查询 parquet 文件。
-- create a view over the parquet fileCREATE VIEW people AS SELECT * FROM read_parquet('test.parquet');-- query the parquet fileSELECT * FROM people;
参考资料
[1]
博客文章: https://duckdb.org/2021/06/25/querying-parquet.html
到顶部