1.Flink-paimon问题
(1) java.lang.IllegalArgumentException: Sink materializer must not be used with Paimon sink. Please set 'table.exec.sink.upsert-materialize' to 'NONE' in Flink's config.
处理报错:
[ERROR] Could not execute SQL statement. Reason:
java.lang.IllegalArgumentException: Sink materializer must not be used with Paimon sink. Please set 'table.exec.sink.upsert-materialize' to 'NONE' in Flink's config.
解决:执行语句前,增加set 'table.exec.sink.upsert-materialize'='NONE';
这里注意执行涉及回撤操作upsert语句时,要关闭flink的upsert-materializer,该配置主要用于是flink优化upsert回撤动作中的一些重复操作,但是目前paimon,不支持该优化,需要保持原始状态。
2.Paimon数据丢失的情况和处理方案
(1) Checkpoint失败且强制重启,未提交数据丢失。
Flink写Paimon资源分配不足导致CPU 100%,CheckPoint超时失败,直接强制重启任务,导致最新一次CheckPoint之后的数据丢失。
原因:Flink写Paimon只有在任务的Checkpoint成功后才会commmit,失败后强制重启,内存缓冲区直接丢弃。
解决方案:调大CPU和内存分配资源
(2) partial-update模式下会忽略delete消息,上游数据删除后,下游不会删除。
原因:partial-update模式的设计初衷是只更新指定字段,而不是处理整行数据的删除
解决方案:如果想感知数据删除,业务上需要指定删除标记字段,让下游能识别上游的删除信号。例如:在表中添加一个专门用于标记删除的字段(如is_deleted),并配置Paimon识别该字段。
(3) sequence.field字段配置错误,导致旧数据把新数据覆盖。
原因:排序字段选择错误,数据乱序导致数据错误。
解决方案:选择单调性字段(如时间戳)作为sequence.field,多流合并时,每条流单独配sequence字段,不要共用。
(4) 快照过期太快,作业Failover超过2h,流读作业恢复时找不到文件。
原因:snapshot.time-retained默认 1h,快照被清理。
解决方案:调整snapshot.time-retained建议8h或24h,保证保留时间≥最大停机时间,并流读表一定加 consumer-id,这个月快照清理的时候会检查有没有依赖它的consumer。
(5) 2个作业并发写同一个bucket,第2个任务持续失败。
这个问题之前提过
原因:Paimon对同桶并发写仅保证Snapshot Isolation,冲突时让其中一方无限重试直到超时,看上去就像"丢数"。
解决方案:
- 一个表只让一个作业写,表加
write-only=true - 必须双写时,用不同的桶字段或
dynamic-bucket=true。
3.Partial Update相关问题
(1) 用FlinkSQL去消费Paimon的partial update表,读不到数据呢?
现象:FlinkSQL一条数据都没有消费到,而Paimon的pu表中却持续有数据
原因:Paimon的pu表,我们是多流写入,并且开启了write-only=true,因此,它只负责写,不负责compaction,而查询是完全依赖compaction的,我们此时还未配置专用压缩任务,因此,无法消费到数据
解决方案:开启单独的专用压缩任务,每一次compact成功,那么,消费端就能消费到变更日志了
SET 'execution.checkpointing.interval'='200s';
SET 'execution.checkpointing.tolerable-failed-checkpoints'='60';
SET 'taskmanager.memory.managed.fraction'='0.05';
SET 'taskmanager.memory.task.off-heap.size'='1G';
SET 'heartbeat.timeout'='6000s';
SET 'execution.checkpointing.timeout'='720m';
SET 'execution.runtime-mode' = 'streaming';
CALL sys.compact(
`table` => 'test_db.test_pu',
options => '
snapshot.expire.limit=5000,
snapshot.num-retained.min=300,
snapshot.num-retained.max=700,
snapshot.time-retained=20h,
sink.parallelism=32,
full-compaction.delta-commits=5000,
target-file-size=512mb,
write-buffer-spillable=true,
write-buffer-size=128 mb,
num-sorted-run.stop-trigger=2147483647,
sort-spill-threshold=10,
lookup.wait=false
'
);
(2) Paimon-core 1.0.1 BUG - partial-update下的aggregate-funciton问题
现象如下:
表的options如下
"merge-engine" : "partial-update",
"fields.has_show.sequence-group" : "show_info",
"fields.default-aggregate-function" : "last_non_null_value",
"fields.deep_converts.aggregate-function" : "sum",
表是Partial Update表,并且由has_show进行sequence-group绑定维度字段的,但是没有绑定指标deep_converts,导致合并的时候,有一部分的deep_converts数据没有被合并,那么读取的时候merge就出现了问题,大概原理如下
读与不读sequence-group绑定字段没有进行统一处理
解决方案:如果是pu-agg表,必须要用sequence-group绑定才可以进行聚合操作,升级Paimon-core至1.3,未绑定则DDL建表的时候就会报错[ERROR] Could not execute SQL statement. Reason: java.lang.IllegalArgumentException: Must use sequence group for aggregation functions but not found for field views.Partial Update模式下,所有使用聚合函数(非默认last_non_null_value)的字段,必须通过sequence group指定 “版本控制字段” 也就是说,所有聚合的字段都必须由sequence-group指定,修改后的options如下
"merge-engine" : "partial-update",
"fields.has_show.sequence-group" : "show_info,deep_converts", -- deep_converts在这里绑定has_show,然后下面才能对其创建aggregate-function
"fields.default-aggregate-function" : "last_non_null_value",
"fields.deep_converts.aggregate-function" : "sum",
4.Compaction问题
(1) 1.0版本Parquet格式报错:ParquetDecodingException: Failed to read 4 bytes并且ORC也报错ExecutionException: java.lang.NullPointerException
报错详情:
Caused by: org.apache.paimon.shade.org.apache.parquet.io.ParquetDecodingException: Failed to read 4 bytes
....
Caused by: java.io.EOFException
去官网issue查询,发现一个解决方案是:改用ORC存储
于是,我设置了format = 'orc',基本不报错了,正当我以为解决问题的时候,数据量一大,发现ORC也频繁报错Caused by:java.util.concurrent.ExecutionException:java.lang.NullPointerException
报错详情如下:
Caused by: java.lang.RuntimeException: Exception happens while executing compaction.
....
Caused by: java.util.concurrent.ExecutionException: java.lang.NullPointerException
这就不是简单的存储问题了,说明是数据自身有问题了,如果数据全部都是null,那么就会出现这个报错
-- 表的结构 大概如下
主键部分
sequence-group标记字段(假设叫a)
a绑定的字段b(b做sum操作)
经排查:发现因为回撤流-U的存在,导致数据被回撤了,那么所有的非聚合字段(主键等维度字段)都是null,聚合字段进行反聚合操作(详情看源码解析:Paimon源码解读 -- PartialUpdateMerge),而如果代码中存在where条件,把+U操作过滤掉了
(比如where costs > 0,如果costs从10变成了0,那么+U costs=0这条数据就被过滤掉了,而-U costs=10就能正常执行回撤),那么,这条数据在表中就是null的情况,文件也就是空文件,因此,parquet和orc都会报错
因此,针对回撤流和where条件导致的数据null问题,解决方案如下
-- 在paimon1.3之后的版本,有一个参数可以忽略-U的操作,不执行回撤,只执行+U
就是配置'ignore-update-before' = 'true',可以解决该问题
--此外,需要检查代码,不能让其把+U操作过滤掉
如上面案例 costs从10变成0了,那么就需要把where costs >0逻辑改为where costs >=0,让其成功执行+U
5.消费Paimon问题
(1) 使用flinkSQL,配置consumer-id去消费Paimon表的changelog,source算子一直红,且并行度一直为1
现象如图:
单独配置的
scan.parallelism、scan.infer-parallelism、scan.split-num都无法对source生效,而是只能对下一个算子生效
原因:consumer模式下的MonitorSource可以理解为一个监听触发器,这个MonitorSource 是一个单并行度(non-parallel)的监控任务,承担以下三个核心职责:
- 监控快照:持续监控 Paimon 表的快照变化
- 创建分片:根据数据文件创建对应的 Split 对象
- 分配任务:将分片分配给下游的 ReadOperator 进行并行处理
所以它只负责监听,并把分片分配给下游的算子去处理,因此,它的并行度就是1
busy的问题,在新版本1.3、1.4已经解决了
(2) 消费lookup/full-compaction模式下的changelog,写入下游明细表报错
报错:Caused by: org.apache.flink.table.api.TableException: The sink for table 'paimon\_catalog.default.test\_to\_sr' has a configured parallelism of 4, while the input parallelism is 32. Since the configured parallelism is different from the input's parallelism and the changelog mode is not insert-only, a primary key is required but could not be found.
原因:
- test_to_sr表配置了32个桶,但是下游消费的并行度只给了4,太小了;
- paimon的lookup的changelog如+I +U -U是基于主键的行级变更产生的,那么必然只能写入到主键表,无法写入到明细表,除非你的changlog是input或none;
解决方案:
- 重新配置消费端的
scan.parallelism和桶数最好保持一致; - 要写入的下游表必须要配置
PRIMARY KEY ();
6.客户端sql操作相关问题
(1) 报错:SQL 错误 [12000] [HY000]: SQL 解析异常:line 1:43 mismatched input:$buckets
原因: 因为$buckets被解析器认为是特殊关键字或非法字符。在大多数SQL方言中,$符号通常有特殊含义。
解决方案:用``包装起来,如下
SELECT * FROM `p_search_dwd_join_query_test3$buckets`
7.Paimon分区标记完成相关报错
(1) 报错Caused by: java.lang.IllegalArgumentException: Table should enable metastore.partitioned-table
原因:DDL的时候配置的partition.mark-done-action=done-partition,这个done-partition是需要去Hive HMS同步元数据的
解决方案:done-partition必须额外配置metastore.partitioned-table = true这样才能把paimon的done标记后的分区同步到Hive中,实现Hive中也能展示该done分区,从而进行离线操作