头图
版本日期备注
1.02024.2.26文章首发
1.12024.8.15补充部分内容
数据湖科普相关的内容我将其做成了视频,喜欢看视频的同学可以在B站上搜索“抽象狗哥”观看相应的内容。

最近我司开始探索Paimon在部分场景下的使用,因此我这边需要做一些技术储备,慢慢关注起来Paimon的一些实现细节。

简单介绍一下前辈Iceberg

一般的数据湖都会设计成开放通用的,即不和特定的存储、计算引擎(比如Spark和Flink)绑定。所以数据湖的定位是在计算引擎之下,又在存储之上,将其称之为table format。

这种设计可以让数据湖更快、更省成本的与现有基础设施集成起来。

需要强调的是数据湖一般是面向OLAP场景的,所以一般存储会选择分布式文件系统。现在OLAP底层存储支持对象存储基本算是快成业界共识了,这玩意儿挺划算的。

数据湖的前辈基本就是Hive了。当时大家用Hive碰到的问题就是Hive耦合HDFS很厉害,最主要体现在:

  1. Hive上的计算执行首先依赖于list操作。在对象存储上做list是个很慢的操作。
  2. Hive的写数据依赖于rename。但同样这个操作在对象存储上做特别的慢。

这两个问题直接导致无法降本。从这点上来说,Iceberg是自己维护了一套元数据,这块网上非常的全,就不再赘述了,google上搜iceberg file layout一大把。

Hive还有其他的问题,如:

  1. metastore的瓶颈问题。
  2. 没有ACID保证。
  3. parition字段必须显示的在query里。
  4. 下推能力有限。Hive只能通过partition和bucket对需要扫描哪些文件进行过滤,无法更加细致。尽管parquet文件里保存了max和min值可以用于进一步的过滤,但没软用。

Iceberg把这些都解了。基于快照实现事务、数据的更新,快照的数据也允许跳版本读取来做时间回溯。同时收集的统计信息也更加细粒度,不仅仅是文件parition级别的,还会记录文件级的内容(比如一个文件中的min、max值)和实现文件内容级的信息——一个文件中的min、max等等。

听起来一切都还很美好。唯一美中不足的就是Iceberg对于实时场景支持得不好:

  • Flink写入Iceberg会引发小文件的问题。
  • Iceberg不支持CDC(OLAP支持CDC的确有点离谱,但是的确有需求呀)。
  • Iceberg主键表不支持部分字段更新。这在实时数仓的场景中有点离谱。

Paimon可以解决什么问题

目前看来Paimon基于Iceberg的场景上,去支持流读流写(这块后续会做源码分析),甚至还支持了点查和预聚合。本质是分布式文件系统上套了一个LSM,这样数据都是有序写入——将多次写入优化成一次顺序写入,对存储系统上比较友好的。同时LSM可以作为一个简单的缓存,且有序写入为后面查询也可以减少代价,这都可以为查询减少代价。

从场景上来说它可以解决一些准实时业务的场景。因为基于对象存储来做底层存储,尤其还是列式存储。无论如何都不好做到实时场景:

  • Paimon的CDC根据不同的模式,会有不同的新鲜度。发出完整CDC的模式要选择Lookup。一般是Checkpoint的间隔+10s多新鲜度,这是比较好的性能考量下。具体还要看数据量、分桶数、小文件数量的影响。
  • 实时场景要求是毫秒级点查响应。Paimon支持的是秒级点查。
但现实中真正需要实时类场景的业务有多少呢?因为数据的新鲜度往往和业务决策周期有关系。这么来看,数据新鲜度的要求从高到低,对于业务场景的总数来说,一定是一个金字塔形状的。

我们前面提到过数据湖一般不会和任何计算引擎绑定。因此业界还有一种玩法叫湖上建仓,计算能力用的是OLAP,数据来自数据湖。这样就很有想象力了,因此现在一些实时+离线的场景会用不同的存储引擎,那么数据就会拷贝好几份。如果数据都放在同一个数据引擎中,这样可以减少不少的存储成本。(对于通用型设计 这块后续会做源码分析

具体实现还是看对于性能的要求的:

  • 要求低就做一些简单的优化直接捞数据。
  • 再高点就缓存到OLAP里。
  • 再高点就不仅仅是缓存到OLAP里,还会做物化视图。

Trade Off

Serving、Trascantion、Analytics

根据业界常识,我们会发现:

  • 面向在线应用,高并发、快速、简单,如:HBase、Redis
  • 面向分析的,大规模数据扫描、过滤、汇总,如:Hive、Presto
  • 面向事务、随机读写的,如:MySQL,PostgreSQL

数据湖是典型的OLAP产物。但结合上文,Paimon具有一定的随机读写能力。

Buffer、Mutable、Ordered

存储结构有三个常见变量:是否使用缓冲、使用不可变的还是可变的文件,以及是否按顺序存储值(有序性)。

由于TiDB底层的RocksDB用了LSM。因此使用了缓冲、不可变性以及顺序性。

RUM

有一种流行的存储结构开销模型考虑了如下三个因素:读取(Read)、更新(Update)和内存(Memory)开销。它被称为RUM猜想。

RUM猜想指出,减少其中两项开销将不可避免地导致第三项开销的恶化,并且优化只能以牺牲三个参数中的一个为代价。我们可以根据这三个参数对不同的存储引擎进行比较,以了解它们针对哪些参数进行了优化,以及其中隐含着哪些可能的权衡。

一个理想的解决方案是拥有最小的读取开销,同时保持较低的内存与写入开销。但在现实中,这是无法实现的,因此我们需要进行取舍。

Paimon允许在配置中自由设置LSM的高度,以便获取读与写之前的权衡。

内幕鸟瞰

前面说到过,计算部分是依赖于计算引擎实现的,本身Paimon没有提供计算能力。存储则是基于部分是文件系统做了薄薄的一层LSM。

从文件布局来看,Partition相当于是一级索引,和Hive一样。Bucket为二级索引,每个Bucket下都会有Data file和Change log file。这意味着如果命中了Parition和Bucket条件,有一些额外的条件查询也不会太慢——一般都会收集文件级的统计信息,并对文件的Reader做一些过滤优化。

整体的布局还是和Iceberg挺像的,这边不再过多赘述。

小结

在这篇文章中我简单的介绍了一下Paimon要解决的问题,以及它的前辈Iceberg的强大与不足之处。

目前该项目还处于孵化中,后续我会一直关注其实现细节,敬请期待。


泊浮目
4.9k 声望1.3k 粉丝