1.文件布局
案例:
INSERT INTO paimon_table VALUES (1, 10001, 'varchar00001', '20230501');
- 数据文件:
data/data-00001.parquet
(存储数据记录,在bucket下面)。 - Manifest 文件:
manifest/manifest-xxxx.avro
(记录数据文件路径、行数、主键范围)。 - Snapshot 文件:
_snapshots/snapshot-1.json
(记录快照信息)
(1) Snapshot
关键1:离线读Snapshot
paimon可以快照捕获在某个时间点的状态。可以去访问指定时间版本时候的表情况。 snapshot存储的是元数据链,不是数据,存的都是manifest list 所有快照文件都存储在快照目录中。
snapshot文件是一个 JSON 文件,包含有关此快照的信息,包括:
- 正在使用的Schema文件
- 包含此快照的所有更改的清单列表(manifest list 会产生两个list,具体介绍如下)
- base 指向的manifest list文件会记录本次快照是以哪些manifest file以为基础的(历史)。
- delta 指向的manifest list文件会记录本次快照是新增了哪些manifest file(新增)。
{
"version" : 3,
"id" : 1,
"schemaId" : 0,
"baseManifestList" : "manifest-list-4ccc-c07f-4090-958c-cfe3ce3889e5-0",
"deltaManifestList" : "manifest-list-4ccc-c07f-4090-958c-cfe3ce3889e5-1",
"changelogManifestList" : null,
"commitUser" : "7d758485-981d-4b1a-a0c6-d34c3eb254bf",
"commitIdentifier" : 9223372036854775807,
"commitKind" : "APPEND",
"timeMillis" : 1684155393354,
"logOffsets" : { },
"totalRecordCount" : 1,
"deltaRecordCount" : 1,
"changelogRecordCount" : 0,
"watermark" : -9223372036854775808
}
(2) Partition和Bucket
分区-->分目录
分桶==>也是分目录,也就是说在分区目录下面还会有分桶目录。桶是读写的最小存储单元,桶的数量限制了最大处理的并行度,建议每个桶的数据大小为1GB左右 注意:
- 对于两个同时修改表的writer,只要不修改同一个bucket,他们的提交都是可以序列化的;
- 但是如果修改的是同一个bucket,则仅能保证快照隔离,也就是说,表的最终状态是两次提交的混合,但不会丢失任何修改
(3) Manifest Files
所有清单列表(manifest list)和清单文件(manifest file)都存储在清单(manifest)目录中。
- 清单列表(manifest list)是清单文件名(manifest file)的列表。
- 清单文件(manifest file)是包含有关 LSM 数据文件和更改日志文件的文件信息。例如对应快照中创建了哪个LSM数据文件、删除了哪个文件。
与manifest list不同,manifest file,并不都是本次快照产生的,可能是以前的产生的,但是被本次快照继承了下来,一般base关联的都是旧的,delta关联的都是新产生的,上面已经讲过了。
每个manifest file都关联了一个或多个具体的数据文件。
记住:manifest file本身没有任何数据,只是数据的描述,记录哪些数据文件包含了哪些数据等信息
(4) Data Files
数据文件按分区和存储桶分组。每个存储桶目录都包含一个 LSM 树及其变更日志文件changelog。目前,Paimon 支持使用 orc(1.0以前默认)、parquet(1.0及以后默认)和 avro 作为数据文件格式。
Paimon中采用的是LSM树的Sorted Run存储
格式如下:
LSM 树将数据文件组织成多个Sorted Run。Sorted Run由一个或多个数据文件组成,并且每个数据文件恰好属于一个Sorted Run。
数据文件中的记录按其主键排序。
- 同一个Sorted Run不同的数据文件的主键范围永远不会重叠;如上图df#1和df#2在不存在主键相同的数据
- 不同的Sorted Run可能具有重叠的主键范围,甚至可能包含相同的主键;如上图df#1和df#3中都有主键为3的数据,一个是apple,一个是banana
问题来了,这样读的话,如果不处理,就读出两条,一个是3-apple;一个是3-banana,这样肯定不行,怎么办呢?
解决:查询LSM树时,必须合并(compaction)所有Sorted Run,并且必须根据用户指定的合并引擎和每条记录的时间戳来合并具有相同主键的所有记录。
写入LSM树的新记录将首先缓存在内存中。当内存缓冲区满时,内存中的所有记录将被排序并刷新到磁盘。(类似HBase,HBase也是基于LSM树的)
读的时候会先合并
compaction会生成新的snapshot,每五个sorted-run就会产生一个小压缩
当越来越多的记录写入LSM树时,Sorted Run的数量将会增加。由于查询LSM树需要将所有Sorted Run合并起来,太多Sorted Run将导致查询性能较差,甚至内存不足。为了限制Sorted Run的数量,我们必须按照一定规则将多个Sorted Run合并为一个大的Sorted Run。这个过程称为Compaction。
过于频繁的Compaction可能会导致写入速度变慢。这是查询和写入性能之间的权衡。 Paimon 目前采用了类似于 Rocksdb 通用压缩的Compaction策略。
默认情况下,当Paimon将记录追加到LSM树时,它也会根据需要执行Compaction。用户还可以选择在“专用Compaction作业”中独立执行所有Compaction。
Full Compaction 是比Compaction更重的合并,是针对整个表的全部数据进行整理,彻底将所有小文件合并,提高整体存储效率。但是相对的Full compaction的付出的执行代价也较大,所以执行的频率也要相对控制。
目前已有主要控制参数为manifest.full-compaction-threshold-size 这个是manifest的总大小。更有效的直接控制是通过full-compaction.delta-commits 目前没有默认值,生产上可以设置几十到上百。
2.数据变更--Changelog
先说结论
关键2:实时读Changelog
目前的changelog的生成方式主要看changelog-producer这个选项:
- none:只保留snapshot级的变更日志,会丢失一部分中间变化。虽然flink本身也会辅助生成changelog但是开销太大不推荐。可以通过
scan.remove-normalize = true
来关闭flink自身的补偿日志。 - input:保留上游输入的变更日志,一般上游输入为CDC时使用(了解数仓分层的大概想到了,ods→dwd|dim可以,但是dwd→dws由于存在聚合,不能用dwd去给dws填充changelog)
- lookup:在上游没有输入CDC日志时,依据paimon对数据的对比生成新的changelog供下游使用,在上游非cdc任务时,算是最准确的changelog(dwd→dws可以用,专门有一个比较器去记录变化然后加入到log中,缺点性能开销大)
- full-compaction:相比lookup生成changelog间隔较大,可以通过设置full compaction间隔,在full compaction时产生最新的changelog给下游,性能相对lookup较好,但产生延迟受full compaction影响。如果设置了该类型的changelog-producer,则full-compaction.delta-commits在无默认值自动变为1(缺点:数据延迟大)。
3.元数据--Catalog
关键3:catalog无缝兼容hive
Paimon Catalog可以持久化元数据,当前支持两种类型的metastore:
Ø 文件系统(默认):将元数据和表文件存储在文件系统中。
Ø hive:在 hive metastore中存储元数据。用户可以直接从 Hive 访问paimon表。
catalog可以理解为在database上面的一层
先说本地文件系统
-- 显示所有的catalog
show catalogs;
-- 创建catalog
CREATE CATALOG fs_catalog WITH (
'type' = 'paimon',
'warehouse' = 'hdfs://hadoop102:8020/paimon/fs'
);
-- 使用catalog
USE CATALOG fs_catalog;
再说使用hive的metastore
1)上传 hive-connector
将flink-sql-connector-hive-3.1.3_2.12-1.17.0.jar上传到Flink的lib目录下
2)重启yarn-session集群
3)启动hive的metastore服务
nohup hive --service metastore &
4)创建Hive Catalog
CREATE CATALOG hive_catalog WITH (
'type' = 'paimon',
'metastore' = 'hive',
'uri' = 'thrift://hadoop102:9083',
'hive-conf-dir' = '/opt/module/hive/conf',
'warehouse' = 'hdfs://hadoop102:8020/paimon/hive'
);
USE CATALOG hive_catalog;
5)注意事项
使用hive Catalog通过alter table更改不兼容的列类型时,参见 HIVE-17832。需要配置
vim /opt/module/hive/conf/hive-site.xml;
<property>
<name>hive.metastore.disallow.incompatible.col.type.changes</name>
<value>false</value>
</property>
上述配置需要在hive-site.xml中配置,且hive metastore服务需要重启。
如果使用的是 Hive3,请禁用 Hive ACID:
hive.strict.managed.tables=false
hive.create.as.insert.only=false
metastore.create.as.acid=false
总结,三个关键点:
- 关键1:离线读Snapshot
- 关键2:实时读Changelog
- 关键3:catalog无缝兼容hive