创建Paimon主键表
操作场景
创建表时指定主键(Primary Key),则创建的表为主键表;主键由列组成,且主键值须唯一。主键表支持insert、update、delete操作。
在存储桶内Paimon数据按主键排序后保存,过滤查询主键时查询性能高。主键表使用LSM树结构存储数据,支持高效读写。
语法结构
例如,创建一张分区键为pt,主键为pt, id,分桶为4的主键表:
-- SPARK SQL
CREATE TABLE my_table (
id INT,
a STRING,
pt STRING
) TBLPROPERTIES (
'primary-key' = 'id',
'bucket' = '4'
); Paimon主键表中每行数据的主键值各不相同,如果将多条具有相同主键的数据写入Paimon主键表,将根据数据合并机制对数据进行合并,相关参数介绍请参见表3。
分桶
分桶(Bucket)是Paimon表读写操作的最小单元。非分区表的所有数据以及分区表每个分区的数据,都会被进一步划分到不同的分桶中,以便多个并发同时进行读写,提高数据读写性能。用户可以通过bucket-key指定分桶列,如果未指定则使用主键作为Bucket Key。支持表1中的分桶类型。
| 类型 | 分桶定义 | 说明 |
|---|---|---|
| 动态分桶(默认) | 创建Paimon主键表时,不指定Bucket或指定'bucket' = '-1'。 |
|
| 固定分桶 | 创建Paimon主键表时,在参数中指定'bucket' = '<num>',即可指定非分区表的分桶数为<num>,或者分区表单个分区的分桶数为<num>。<num>是一个大于0的整数。 |
|
动态分桶表更新
| 类型 | 说明 |
|---|---|
| 跨分区更新的动态分桶表(主键不完全包含部分分区键) | Paimon无法根据主键确定该数据属于的分区及分桶,需要内存中维护主键与分区以及分桶编号的映射关系。相比固定分桶而言,数据量较大的表可能会产生明显的性能损失。 数据合并机制会对跨分区更新的结果产生如下不同影响:
|
| 非跨分区更新的动态分桶表(主键包含全部分区键) | Paimon可以确定该主键属于的分区,但无法确定属于的分桶,需要使用额外的堆内存创建索引,以维护主键与分桶编号的映射关系。 |
数据合并机制
Paimon提供多种合并引擎,支持不同的合并机制,适用于不同的场景。默认情况下,Paimon会按照数据的输入顺序确定数据合并顺序,最后写入的数据会被认为是最新数据。如果输入数据流存在乱序,可以通过在表属性中设置'sequence.field' = '<column-name>',具有相同主键的数据会按照<column-name>列的值从小到大进行合并。可以设置Sequence Field属性的数据类型有:TINYINT、SMALLINT、INTEGER、BIGINT、TIMESTAMP和TIMESTAMP_LTZ。
| 类型 | 说明 | 适用场景 |
|---|---|---|
| 去重(Deduplicate) | 默认合并引擎,仅保留相同主键最新记录。如果最新记录是DELETE记录,则所有具有相同主键的记录都将被删除。可以通过配置ignore-delete=true来忽略删除操作。 | 适用于需要确保主键唯一性,并且只关心最新数据的场景。 |
| 部分更新(Partial Update) | 允许通过多次更新逐步完善记录的各个列,即根据相同主键,逐个更新字段的最新值,但不会覆盖非空值。默认情况下,部分更新引擎不接受删除记录,可以通过配置ignore-delete来忽略删除记录,或配置sequence-group来撤销部分列。 | 适用于需要逐步更新记录的部分字段,而不影响其他字段的场景。适合主键多流Join的场景。 |
| 预聚合(Aggregation) | 根据指定的聚合函数,对具有相同主键的记录进行字段级别的聚合。每个非主键字段可以指定聚合函数,未指定的字段默认使用last_non_null_value聚合。 | 适用于需要对数据进行聚合计算的场景,例如求和、计数等。 |
| 首行(First Row) | 保留具有相同主键的第一条记录,与去重引擎不同,首行引擎保留最早的记录。 | 适用于需要保留首次出现的记录,而忽略后续更新的场景。 |
Changelog产生机制
流式写入时,通过指定changelog-producer表属性,可选择从表文件生成变更日志的方式。启用changelog producer会显著降低compaction性能,非必要不启用。
| 类型 | 说明 | 适用场景 |
|---|---|---|
| none(默认) | 默认模式,不生成额外的变更日志。Paimon源只能看到跨快照合并后的更改,无法形成完整的变更日志。 | 适用于不需要完整变更日志的消费者,例如数据库系统。 |
| input | 直接使用输入记录作为完整变更日志源,保存到独立的changelog文件中,必须输入完整的变更日志。 | 适用于输入本身就是完整的变更日志的情况,例如来自数据库的 CDC(变更数据捕获)数据,或由Flink有状态计算生成的数据。 |
| lookup | 在提交数据写入之前,通过lookup生成变更日志。Paimon会在内存和本地磁盘上缓存数据,以生成完整的变更日志。 可以设置changelog-producer.row-deduplicate=true避免对相同记录生成-U/+U,设置该参数会引入额外计算,推荐仅在无效变更数据较多情况下使用该参数。 lookup在内存和本地磁盘缓存数据,通过以下参数进行性能调优:
| 适用于输入无法生成完整变更日志,但希望避免昂贵的 “Normalize” 操作的情况。需要注意的是,Lookup操作会消耗一定的资源,比较适用于实时性要求较高场景(分钟级)。 |
| full-compaction | 通过比较完全压缩之间的结果,生成差异作为变更日志。变更日志的延迟受到完全压缩频率的影响,可以通过full-compaction.delta-commits(Flink)、compaction.optimization-interval(Spark)参数控制。 可以设置changelog-producer.row-deduplicate=true避免对相同记录生成 -U/+U,设置该参数会引入额外计算,推荐仅在无效变更数据较多情况下使用该参数。 | 适用于希望解耦数据写入和变更日志生成的高延迟场景,例如每隔一段时间(如小时级)生成一次变更日志。此模式可以减少资源消耗,但会增加变更日志的延迟。 |