Paimon 作为实时数据湖,除了支持 Flink 和 Spark 引擎外,同样支持 ByteHouse、Trino、Presto、Doris/StarRocks 等丰富的 OLAP 产品作为查询引擎。得益于 Paimon 的 LSM-Tree 结构设计,往往 OLAP 引擎查询 Paimon 数据湖会有很好的主键查询性能。本文主要介绍 Paimon 的查询优化常见的概念和手段。
Paimon 表类型分为主键表和非主键表(Append)表。
非主键表类似于传统的 Hive 表,没有主键设置。值得注意的是,Paimon 的 Append 表在 checkpoint 提交数据。这种强一致性保证,不会因为 Paimon 任务 checkpoint 失败而导致数据丢失或者重复,可以达到 Exactly-Once。
主键表的文件结构大致如下所示,表或分区包含多个桶(bucket),每个桶是一个独立的 LSM 树结构,包含多个文件。LSM 的写入过程大致如下:Flink checkpoint 会刷新 L0 文件,并根据需要触发 compaction 以合并数据。根据写入时的处理方式不同,有以下三种模式:
'full-compaction.delta-commits' = '1'
,同步进行 full compaction,即在写入时完成合并。
'deletion-vectors.enabled' = 'true'
,在写入阶段,LSM 会查询并生成数据文件的 deletion vector 文件,在读取时直接过滤掉不必要的行。
表模式对查询性能的影响非常大。一般情况下,主键表为了较好的查询性能,比较推荐使用 MOW(即开启 Deletion Vector)表。大致几种表特点如下:
对于常规的分桶表(例如,bucket = 5),主键的过滤条件将大大加速查询并减少大量文件的读取。所以可以如果是经常性通过主键进行点查,所以合理设计主键字段是非常有效的选择。
另外主键采取前缀索引的方式,所以设计主键索引的时候,尽量将可能单独查询的字段放在前面。比如 region + userid 作为主键,如果 region 是一定会带有的查询条件,而 userid 不会作为单独的查询条件进行查询的话,就建议主键定义 region 在 userid 之前。
Deletion Vector 在主键表场景下,通过记录指定文件中的记录需要被 Delete 的记录(比如主键表 Insert 记录时查询已有主键,如有则需在 Deletion Vector 文件中记录)。
基于上述的 Merge On Write,所以在 Read 阶段无须在进行 Merge,只需要综合 Data 文件 + Deletion Vector 的结果,实现并发不再受限于 Bucket 数量,最高可到文件数量,极大提高 OLAP 读取效率;
您可以在启用 Deletion Vectors 的表中使用文件索引。Paimon 的 Data File 支持创建对应的 File Index 文件,当前 Paimon 版本支持 Bloom Filter,可以快速判断某个文件中是否包含某个字段值,显著提高对应列值在 Data File 中的 SCAN 效率;
CREATE TABLE <PAIMON_TABLE> WITH ( 'deletion-vectors.enabled' = 'true', 'file-index.bloom-filter.columns' = 'c1,c2', 'file-index.bloom-filter.c1.items' = '200' );
支持的过滤器类型:Bloom Filter
:
file-index.bloom-filter.columns
:指定需要 Bloom Filter 索引的列。file-index.bloom-filter.<column_name>.fpp
:配置误报率。file-index.bloom-filter.<column_name>.items
:配置一个数据文件中预期的不同项的数量。因为 Flink Paimon 的 Checkpoint 提交机制,可以保证写入 Paimon Append 表数据不丢失不重复。所以如果业务中如果没有主键去重需求的时候,可以不用担心 Flink Failover 引入重复数据。
在大部分情况下,Append 表可以大幅提升写入性能,也因不受并行度限制,也可以保障较好的查询性能。但因为 Append 表底层无序的,所以通过主键字段范围查询、点查性能还是比主键表要弱。
如果您需要极致的读取性能,并且可以接受读取略微过时的数据,您可以使用ro
(读优化表 (Read-optimized Table))系统表。读取优化的系统表通过仅扫描不需要合并的文件来提高读取性能。
对于主键表,ro
系统表仅扫描最顶层的文件。也就是说,ro
系统表仅生成最新 Full Compaction 的结果。由于不同的存储桶可能在不同的时间执行 Full Compaction,因此不同键的值可能来自不同的快照。
对于追加表,由于所有文件都可以在不合并的情况下读取,ro
系统表的行为与普通的追加表类似。
SELECT * FROM my_table$ro;