<1> Deduplicate--默认
当 Paimon 接收到两条或更多具有相同主键的记录时,它会将它们合并为一条记录以保持主键的唯一性
记录保留策略:
- 保留最新记录:Paimon 只会保留最新的记录,并丢弃其他具有相同主键的记录。
- 处理 DELETE 记录:如果最新的记录是一条 DELETE 记录,则所有具有相同主键的记录都将被删除。
配置选项:
- ignore-delete :用户可以配置
ignore-delete
参数来忽略 DELETE 记录。默认情况下,DELETE 记录会导致所有具有相同主键的记录被删除,但通过设置ignore-delete=true
,可以忽略 DELETE 记录。
示例配置:
sql
CREATE TABLE my_table (
id INT,
name STRING,
age INT,
PRIMARY KEY (id) NOT ENFORCED
)
WITH (
'merge-engine' = 'deduplicate',
'ignore-delete' = 'true'
);
注意事项:
- 在 Flink SQL 的 TableConfig 中,将
table.exec.sink.upsert-materialize
设置为NONE
,以避免启用upsert-materialize
可能导致的奇怪行为[[1, 4, 17]]。 - 当输入数据无序时,建议使用序列字段(Sequence Field)来纠正乱序[[1, 4, 17]]。
- 如果最新记录是 DELETE 记录,则所有具有相同主键的记录都将被删除,除非配置了
ignore-delete=true
来忽略 DELETE 记录。
<2> aggregation函数
对于paimon而言,实现类似Doris那种聚合模型的功能,只需要三步
- 指定主键
- 指定merge-engine为aggregation
- 指定聚合字段的function
sql
CREATE TABLE my_table (
product_id BIGINT,
price DOUBLE,
sales BIGINT,
PRIMARY KEY (product_id) NOT ENFORCED
) WITH (
'merge-engine' = 'aggregation',
'fields.price.aggregate-function' = 'max',
'fields.sales.aggregate-function' = 'sum'
);
<1, 23.0, 15>
<1, 30.2, 20>
the final result will be <1, 30.2, 35>.
CREATE TABLE my_table (
product_id BIGINT,
name varchar,
price DOUBLE,
sales BIGINT,
PRIMARY KEY (product_id) NOT ENFORCED
) WITH (
'merge-engine' = 'aggregation',
'fields.price.aggregate-function' = 'max',
'fields.sales.aggregate-function' = 'sum'
);
<1, 张三, 23.0, 15>
<1, 李四, 30.2, 20>
最终结果:<1, 李四, 30.2, 35>
《1》常见的聚合函数
max、min、sum、count、lastvalue、last nonnull value、firstvalue、first nonnullvalue、collect
《2》特殊的聚合函数
listagg: 将多个字符串连接成一个字符串,默认用,分隔
bool_and: 评估布尔集中的所有值是否为 true
bool_or: 检查布尔集中是否至少有一个值为 true。
rbm32: 将多个序列化的 32 位 RoaringBitmap 聚合成一个 RoaringBitmap。
rbm64: 将多个序列化的 64 位 Roaring64Bitmap 聚合成一个 Roaring64Bitmap。
nested_update: 将多行收集到一个数组中(所谓的"嵌套表")。用于指定嵌套表的主键。如果没有键,则 row 将被附加到数组中。fields.<field-name>.nested-key=pk0,pk1,...
案例如下:
sql
-- orders table
CREATE TABLE orders (
order_id BIGINT PRIMARY KEY NOT ENFORCED,
user_name STRING,
address STRING
);
-- sub orders that have the same order_id
-- belongs to the same order
CREATE TABLE sub_orders (
order_id BIGINT,
sub_order_id INT,
product_name STRING,
price BIGINT,
PRIMARY KEY (order_id, sub_order_id) NOT ENFORCED
);
-- wide table
CREATE TABLE order_wide (
order_id BIGINT PRIMARY KEY NOT ENFORCED,
user_name STRING,
address STRING,
sub_orders ARRAY<ROW<sub_order_id BIGINT, product_name STRING, price BIGINT>>
) WITH (
'merge-engine' = 'aggregation',
'fields.sub_orders.aggregate-function' = 'nested_update',
'fields.sub_orders.nested-key' = 'sub_order_id' --这是为了将array中相同nested-key的进行覆盖,不同nested-key的进行nested_update追加
);
-- orders
INSERT INTO orders VALUES (1, 'Alice', 'Beijing');
-- sub_orders
INSERT INTO sub_orders VALUES (1, 1, 'iPhone', 5999);
INSERT INTO sub_orders VALUES (1, 2, 'MacBook', 12999);
-- widen
INSERT INTO order_wide
SELECT
order_id,
user_name,
address,
CAST (NULL AS ARRAY<ROW<sub_order_id BIGINT, product_name STRING, price BIGINT>>)
FROM orders -- 1, 'Alice', 'Beijing', null
UNION ALL
SELECT
order_id,
CAST (NULL AS STRING),
CAST (NULL AS STRING),
ARRAY[ROW(sub_order_id, product_name, price)]
FROM sub_orders;-- 1, null, null, [{"sub_order_id":1,"product_name":"iphone","price":5999},
{"sub_order_id":2,"product_name":"MacBook","price":12999}]
-- query using UNNEST
SELECT order_id, user_name, address, sub_order_id, product_name, price
FROM order_wide, UNNEST(sub_orders) AS so(sub_order_id, product_name, price)
补充:select。。。unnest(sub_orders)
作用:
将宽表中的嵌套数组(sub_orders)展开成扁平结构,便于查询。
效果:
类似于 SQL JOIN,但更高效(因为数据已经预聚合)。
最终order_wide数据如下:
{
"order_id":1,
"user_name":"Alice",
"address":"Beijing",
"sub_orders":[
{"sub_order_id":1,"product_name":"iPhone","price":5999},
{"sub_order_id":2,"product_name":"MacBook","price":12999}
]
}
查询的结果如下
order_id user_name address sub_order_id product_name price
1 Alice Beijing 1 iPhone 5999
1 Alice Beijing 2 MacBook 12999
**mergemap: **mergemap 函数 merge input maps。它仅支持 MAP 类型。
<3> first row
- 功能概述
通过指定 'merge-engine' = 'first-row'
,用户可以保留同一主键的第一行数据。这与 deduplicate
合并引擎不同,first-row
引擎只会生成插入类型的更改日志。
- 使用限制
- Changelog Producer :
first-row
合并引擎必须与lookup
changelog producer 一起使用。 - Sequence Field : 不能指定
sequence.field
。 - 消息类型 : 不接受
DELETE
和UPDATE_BEFORE
消息。可以通过配置first-row.ignore-delete
来忽略这两种记录。
- 可见性保证
- 文件级别为0的文件只有在完成合并后才会可见。默认情况下,合并是同步的,如果开启异步合并,可能会有数据延迟。
- 应用场景
first-row
合并引擎在流计算中非常有用,可以替代日志去重功能。
- 配置示例
以下是一个使用 first-row
合并引擎的表创建示例:
sql
CREATE TABLE test (
key INT,
field_a INT,
field_b INT,
field_c INT,
field_d INT,
PRIMARY KEY (key) NOT ENFORCED
) WITH (
'merge-engine' = 'first-row',
'changelog-producer' = 'lookup'
);
注意事项
- 删除向量 : 合并引擎不能是
first-row
,因为first-row
引擎的读取已经没有合并,不需要删除向量。 - 性能优化 : 由于
first-row
引擎的状态数据只存储主键信息,可以显著提高状态访问效率,从而提升实时计算任务的性能。