3 Paimon数据湖中的表类型详解_paimon表和hive区别

  • 1:Fixed Bucket mode:属于固定Bucket数量模式,也就是需要我们手工指定Bucket的数量。我们在建表时默认使用的就是这种模式,Bucket参数的值默认为1。我们只需要给Bucket设置一个大于0的数值即可。但是需要注意:Bucket数量过大会导致小文件过多,影响读取性能;Bucket数量过小会影响写入性能。一般情况下,每个Bucket中的数据量推荐为1G左右。
  • 2:Dynamic Bucket mode:属于动态Bucket数量模式,也就是说Bucket的数量是动态变化的。此时我们需要在建表时指定'bucket' = '-1',此时会由Paimon动态维护索引,将每个Bucket中的数据条数控制在2000000(2百万)以下,这个数值是由dynamic-bucket.target-row-num这个参数控制的。但是需要注意,目前这种模式属于实验性质,暂时不建议在生产环境下使用。

下面我们通过一个案例来具体感受一下Bucket。

创建package:tech.xuwei.paimon.bucket
创建object:BucketDemo

代码如下:

package tech.xuwei.paimon.bucket

import org.apache.flink.api.common.RuntimeExecutionMode
import org.apache.flink.streaming.api.scala.StreamExecutionEnvironment
import org.apache.flink.table.api.bridge.scala.StreamTableEnvironment

/**

  • 验证Bucket特性
  • Created by xuwei
    */
    object BucketDemo {
    def main(args: Array[String]): Unit = {
    //创建执行环境
    val env = StreamExecutionEnvironment.getExecutionEnvironment
    env.setRuntimeMode(RuntimeExecutionMode.STREAMING)
    val tEnv = StreamTableEnvironment.create(env)

//创建Paimon类型的Catalog
tEnv.executeSql(
“”"
|CREATE CATALOG paimon_catalog WITH (
| ‘type’=‘paimon’,
| ‘warehouse’=‘hdfs://bigdata01:9000/paimon’
|)
|“”".stripMargin)
tEnv.executeSql(“USE CATALOG paimon_catalog”)

//创建Paimon表
tEnv.executeSql(
“”"
|CREATE TABLE IF NOT EXISTS bucket_test (
| word STRING,
| cnt BIGINT,
| PRIMARY KEY (word) NOT ENFORCED
|)WITH(
| ‘bucket’ = ‘2’ – 手工指定bucket的值,默认为1
|)
|“”".stripMargin)

//查看最完整的建表语句
tEnv.executeSql(“SHOW CREATE TABLE bucket_test”).print()

//向表中添加数据
tEnv.executeSql(
“”"
|INSERT INTO bucket_test(word,cnt)
|VALUES(‘a’,1) , (‘b’,2) , (‘c’,1) , (‘d’,3)
|“”".stripMargin)

}

}

执行代码,可以看到输出的完整建表语句信息:

CREATE TABLE paimon_catalog.default.bucket_test (
word VARCHAR(2147483647) NOT NULL,
cnt BIGINT,
CONSTRAINT 53dc473d-3e56-4e13-ac8b-a4f3c9abb72b PRIMARY KEY (word) NOT ENFORCED
) WITH (
‘bucket’ = ‘2’,
‘path’ = ‘hdfs://bigdata01:9000/paimon/default.db/bucket_test’
)

接下来我们到hdfs中查看一个这个表的Bucket信息:

[root@bigdata04 ~]# hdfs dfs -ls /paimon/default.db/bucket_test
Found 5 items
drwxr-xr-x - yehua supergroup 0 2028-11-29 17:54 /paimon/default.db/bucket_test/bucket-0
drwxr-xr-x - yehua supergroup 0 2028-11-29 17:54 /paimon/default.db/bucket_test/bucket-1
drwxr-xr-x - yehua supergroup 0 2028-11-29 17:54 /paimon/default.db/bucket_test/manifest
drwxr-xr-x - yehua supergroup 0 2028-11-29 17:54 /paimon/default.db/bucket_test/schema
drwxr-xr-x - yehua supergroup 0 2028-11-29 17:54 /paimon/default.db/bucket_test/snapshot

此时可以看到,这个表中包含2个bucket目录,这两个bucket目录中存储的就是这个表中的所有数据。

bucket目录内部都是一些data数据文件,里面就是真实的数据内容了:

[root@bigdata04 ~]# hdfs dfs -ls /paimon/default.db/bucket_test/bucket-0
Found 1 items
-rw-r–r-- 3 yehua supergroup 545 2028-11-29 17:54 /paimon/default.db/bucket_test/bucket-0/data-8475e141-1725-489f-bd09-13606fd2302f-0.orc

咱们之前创建的表没有手工指定bucket,那么bucket默认为1,可以到表wc_sink_sql里面看一下,这个表里面存储了多条数据,但是只有1个bucket:

[root@bigdata04 ~]# hdfs dfs -ls /paimon/default.db/wc_sink_sql
Found 4 items
drwxr-xr-x - yehua supergroup 0 2028-11-29 11:53 /paimon/default.db/wc_sink_sql/bucket-0
drwxr-xr-x - yehua supergroup 0 2028-11-29 11:53 /paimon/default.db/wc_sink_sql/manifest
drwxr-xr-x - yehua supergroup 0 2028-11-29 11:44 /paimon/default.db/wc_sink_sql/schema
drwxr-xr-x - yehua supergroup 0 2028-11-29 11:53 /paimon/default.db/wc_sink_sql/snapshot

所以在实际工作中,设置合适的bucket数量就是非常重要的了。
但是在不同的时期,bucket的数量大概率是需要调整的,因为合适的bucket数量不可能是一成不变的,随着数据量的增加,bucket的数量也需要增大。

官方支持后期手工调整表中的bucket数量,但是想要对bucket中的数据进行重新分布,则只能通过离线流程来完成,也就是说需要跑一个离线任务来重新对bucket中的数据进行分布。

3.2.1.2 Changelog Producers

Changelog Producers:可以翻译为变更日志生产者。

Paimon表中存储数据的时候,除了存储数据本身,还可以选择存储数据的变更日志,也就是Changelog。

Changelog 主要应用在流读场景,在构建实时数据仓库的过程中,我们需要通过流读取上游的数据写入到下游,完成数仓各层之间的数据同步,让整个数仓的数据实时地流动起来。

如果上游数据来源于MySQL的 Binlog 日志,这样是可以直接提供完整的 Changelog 以供流来读取的。

但是针对湖仓一体架构,数仓分层是在Paimon里面实现的,数据会以表格的形式存储在文件系统中。
如果下游的Flink任务要通过流读取Paimon表中的数据,则需要Paimon的存储系统帮助生成 Changelog,以便下游流读。
此时就需要我们在建表时指定参数changelog-producer来决定在何时以何种方式生成Changelog。

如果不指定Changelog Producer则不会向Paimon表中写入数据的时候生成 Changelog,那么下游任务需要在流读时生成一个Changelog Normalize物化节点来产生Changelog。
这种方式的成本相对比较高,并且官方也不建议这样使用,因为下游任务会在状态中维护一份全量的数据,也就是说每条数据都需要保存在状态中便于任务在执行时生成Changelog。

可能大家在这会有一个疑问,为什么一定需要Changelog呢?

因为通过Changelog可以记录数据的中间变化,针对某些计算逻辑,我们需要知道数据之前的历史值是什么,这样才能得到正确的结果。

例如:我们接收到的数据中包含了相同主键的多条 INSERT 数据,这样会导致下游的流聚合任务有问题,因为相同主键的多条数据应该被认为是更新,而不是重复累加计算。

Paimon 支持的 Changelog Produers主要包括这几种:None、Input、Lookup和Full Compaction

下面我们来详细分析一下:

(1)None

如果不指定changelog-producer,默认就是 none,此时存储数据的时候不会存储数据的Changelog,后期读取数据时会动态生成Changelog,成本较高,不建议使用。
在这里插入图片描述

看这个图,此时这个数据源可以是任意类型的数据源,假设数据源依次产生了+I,+U,-D类型的数据,其实这里面缺少了-U类型的数据,我们通过Paimon 的SinkWriter组件将这些数据写入到了Paimon表中。

注意:此时这个Paimon表中配置的changelog-producer参数的值为none

此时在向Paimon表中写入数据的时候,这个表只会存储数据本身,不会存储数据的Changelog。

当我们再通过一个任务从这个Paimon表中读取数据的时候,这个任务只能读取到+I、+U和-D类型的数据,但是这个任务会产生一个Changelog Normalize物化节点来自己生成数据的Changelog,但是这个操作是非常昂贵的,因为它需要在状态中维护数据的所有历史变化情况来生成数据的Changelog。最终是可以获取到完整的+I、-U、+U、-D类型的数据的。

下面我们来通过一个案例具体演示一下建表语句中指定changelog-producer=none时的效果。

创建package:tech.xuwei.paimon.changelogproducer.none

创建object:FlinkDataStreamWriteToPaimonForNone

这个Object负责向Paimon表中模拟写入数据。

代码如下:

package tech.xuwei.paimon.changelogproducer.none

import org.apache.flink.api.common.RuntimeExecutionMode
import org.apache.flink.api.common.typeinfo.Types
import org.apache.flink.streaming.api.scala.StreamExecutionEnvironment
import org.apache.flink.table.api.bridge.scala.StreamTableEnvironment
import org.apache.flink.table.api.{DataTypes, Schema}
import org.apache.flink.table.connector.ChangelogMode
import org.apache.flink.types.{Row, RowKind}

/**

  • 使用Flink DataStream API向Paimon表中写入数据
  • Created by xuwei
    */
    object FlinkDataStreamWriteToPaimonForNone {
    def main(args: Array[String]): Unit = {
    //获取执行环境
    val env = StreamExecutionEnvironment.getExecutionEnvironment
    env.setRuntimeMode(RuntimeExecutionMode.STREAMING)
    val tEnv = StreamTableEnvironment.create(env)

//手工构造一个Changelog DataStream 数据流
val dataStream = env.fromElements(
Row.ofKind(RowKind.INSERT, “jack”, Int.box(10))//+I
//Row.ofKind(RowKind.UPDATE_AFTER, “jack”, Int.box(11))//+U
//Row.ofKind(RowKind.DELETE, “jack”, Int.box(11))//-D
)(Types.ROW_NAMED(Array(“name”, “age”),Types.STRING,Types.INT))

//将DataStream转换为Table
val schema = Schema.newBuilder()
.column(“name”, DataTypes.STRING().notNull())//主键非空
.column(“age”, DataTypes.INT())
.primaryKey(“name”)//指定主键
.build()
val table = tEnv.fromChangelogStream(dataStream,schema,ChangelogMode.all())

//创建Paimon类型的Catalog
tEnv.executeSql(
“”"
|CREATE CATALOG paimon_catalog WITH (
| ‘type’=‘paimon’,
| ‘warehouse’=‘hdfs://bigdata01:9000/paimon’
|)
|“”".stripMargin)
tEnv.executeSql(“USE CATALOG paimon_catalog”)

//注册临时表
tEnv.createTemporaryView(“t1”,table)

//创建Paimon类型的表
tEnv.executeSql(
“”"
|-- 注意:这里的表名使用反引号进行转义,否则会导致SQL DDL语句解析失败。
|CREATE TABLE IF NOT EXISTS changelog_none (
| name STRING,
| age INT,
| PRIMARY KEY (name) NOT ENFORCED
|) WITH (
| ‘changelog-producer’ = ‘none’ – 注意:值为none时这一行配置可以省略不写
|)
|“”".stripMargin)

//向Paimon表中写入数据
tEnv.executeSql(
“”"
|INSERT INTO changelog_none
|SELECT name,age FROM t1
|“”".stripMargin)
}

}

注意:在执行代码的时候通过修改env.fromElements(...)中的注释来实现实时产生多种类型数据的效果。

接下来创建Object:FlinkDataStreamReadFromPaimonForNone

这个Object负责从Paimon表中实时读取数据。

注意:为了便于在本地观察Flink读取数据任务中自动生成的Changelog Normalize物化节点,所以我们需要在代码中开启本地WebUI功能。

先在pom.xml中引入相关的依赖:

org.apache.flink flink-runtime-web 1.15.0

代码如下:

package tech.xuwei.paimon.changelogproducer.none

import java.time.ZoneId

import org.apache.flink.api.common.RuntimeExecutionMode
import org.apache.flink.configuration.{Configuration, RestOptions}
import org.apache.flink.streaming.api.scala.StreamExecutionEnvironment
import org.apache.flink.table.api.bridge.scala.StreamTableEnvironment

/**

  • 使用Flink DataStream API从Paimon表中读取数据
  • Created by xuwei
    */
    object FlinkDataStreamReadFromPaimonForNone {
    def main(args: Array[String]): Unit = {
    val conf = new Configuration()
    //指定WebUI界面的访问端口,默认就是8081
    conf.setString(RestOptions.BIND_PORT,“8081”)
    //为了便于在本地通过页面观察任务执行情况,所以开启本地WebUI功能
    val env = StreamExecutionEnvironment.createLocalEnvironmentWithWebUI(conf)
    env.setRuntimeMode(RuntimeExecutionMode.STREAMING)

//禁用Chain,把多个算子拆分开单独执行,便于在开发和测试阶段观察,正式执行时不需要禁用Chain
env.disableOperatorChaining()

val tEnv = StreamTableEnvironment.create(env)

//创建Paimon类型的Catalog
tEnv.executeSql(
“”

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值