Flink 通过 paimon 关联维表,内存降为原来的1/4

  1. 了解大厂经验
  2. 拥有和大厂相匹配的技术等

希望看什么,评论或者私信告诉我!

一、前言

线上实时任务,通过 FlinkSQL 关联 Iceberg 维表,维表大搞有 60w,首先通过 FlinkSQL关联 Iceberg 维表上线了,经过一番调优后:TaskManager Memory 给到了 16G,但通过监控可以轻易的发现 Heap 没下来过 10GB

图片.png

二、优化

2.1 分析 Iceberg lookup 部分源码

因为 Iceberg 的 lookup 是公司内部自己实现的,就不贴源码了,但核心一点就是,look up 维表 cache 的数据会存在内存中,这就是为什么堆内存没有下来过 10GB

2.2 切换到 paimon 维表

TaskManager Memory 给到了 4G,程序运行的轻轻松松,另外为了增加 rocksdb 性能,也适当的增加了 rocksdb 的内存

图片.png

为了替换 paimon 后内存可以下降那么多呢?

2.3 paimon 维表原理分析

首先来看一下 FlinkSQL look up paimon 的维表的源码,这里我们以 flink1.15 为例。 下载完 paimon 源码后,找到 moudle paimon-flink-1.15

<think>我们正在处理一个Flink流处理任务,需要读取一个实时更新的Paimon(例如实时订单流),并与一个每天更新一次的分区(例如每日更新的商品)进行Join操作。关键点在于如何高效地关联实时流和每日批量更新的。 解决方案核心思路: 1. **实时(事实)**:使用流模式读取,每条记录都会触发处理。 2. **每日分区)**:由于每天更新一次,我们可以将其视为一个缓慢变化的。在Flink中,我们可以使用Lookup Join,并配置合理的缓存策略以减少对底层存储的访问。 具体步骤: ### 1. 创建实时(事实) 假设实时为`orders_realtime`,包含订单的实时信息,其中`proc_time`是处理时间属性,用于Lookup Join。 ```sql CREATE TABLE orders_realtime ( order_id STRING, product_id INT, order_time TIMESTAMP(3), proc_time AS PROCTIME() -- 处理时间属性,用于Lookup Join ) WITH ( 'connector' = 'paimon', 'path' = 'hdfs://path/to/realtime_orders', 'scan.mode' = 'latest' -- 实时读取最新数据 ); ``` ### 2. 创建每日分区) 假设每日分区为`product_daily`,按天分区,每天批量更新一次。注意,我们需要设置分区时间提取器和刷新间隔。 ```sql CREATE TABLE product_daily ( product_id INT, product_name STRING, price DECIMAL(10, 2), dt STRING -- 分区字段,格式为'yyyy-MM-dd' ) WITH ( 'connector' = 'paimon', 'path' = 'hdfs://path/to/product_daily', 'partition.expiration-time' = '7 d', -- 可选:分区过期策略 'partition.timestamp-formatter' = 'yyyy-MM-dd', 'partition.timestamp-pattern' = '$dt', 'continuous.discovery-interval' = '1 d' -- 每天检查一次分区更新 ); ``` ### 3. 执行Lookup Join 使用处理时间(`proc_time`)进行Lookup Join,确保每个实时订单记录关联的是当前时刻的最新版本(即最新分区)。 ```sql SELECT o.order_id, o.product_id, p.product_name, p.price, o.order_time FROM orders_realtime AS o JOIN product_daily FOR SYSTEM_TIME AS OF o.proc_time AS p ON o.product_id = p.product_id; ``` ### 4. 缓存优化 由于每天更新一次,我们可以使用全量缓存(FULL)策略,避免频繁访问底层存储。 在配置中添加Lookup Cache参数: ```sql CREATE TABLE product_daily ( ... ) WITH ( ... -- Lookup Cache配置 'lookup.cache-type' = 'FULL', -- 全量缓存,适用于更新频率低的 'lookup.cache-ttl' = '24 h', -- 缓存24小时,与分区更新频率一致 'lookup.async' = 'true' -- 异步查询 ); ``` 说明: - `lookup.cache-type='FULL'`:将整个加载到内存中,适用于小内存足够)且更新不频繁的场景。 - `lookup.cache-ttl='24 h'`:缓存24小时,每天自动刷新一次(配合`continuous.discovery-interval`)。 - `lookup.async='true'`:异步查询,避免阻塞主处理线程。 ### 5. 分区更新机制 Paimon的分区在添加新分区(如新一天的数据)后,Flink作业如何感知? - 通过`continuous.discovery-interval`参数控制分区发现的间隔(这里设为1天)。当新分区被检测到时,Lookup Cache会在下一次访问时重新加载全量数据(如果缓存过期)。 注意:缓存刷新是在下次访问时触发,因此`cache-ttl`应设置得大于等于分区更新间隔,以确保在刷新前缓存不会失效。 ### 6. 流批一体下的特殊处理 如果实时也是分区(例如按小时分区),而按天分区,则可能需要动态关联分区路径。但Paimon的Lookup Join会自动处理分区过滤,只需确保的分区字段在Join条件中正确使用(这里我们使用`product_id`作为Join键,分区字段`dt`不直接参与Join,但的最新分区数据会包含所有商品的最新信息)。 如果的分区是按天,且每天的分区包含当天的完整度快照,那么上述Join操作会关联最新分区的数据(即当天数据)。 ### 7. 性能注意事项 - **大小**:如果很大(例如上亿条),全量缓存可能消耗大量内存,需谨慎使用。可考虑: - 使用`'lookup.cache-type'='PARTIAL'`,并调整`cache-max-rows`和`cache-ttl`。 - 增加内存资源。 - **缓存刷新延迟**:新分区加入后,缓存不会立即刷新,而是等到缓存过期或下次访问时触发。如果需要更及时的刷新,可缩短`cache-ttl`(但不宜过短,否则失去缓存意义)。 ### 8. 监控与调优 通过Flink Metrics监控缓存命中率: - `hitCacheCount`:命中缓存的次数 - `totalCount`:总查询次数 - 命中率 = hitCacheCount / totalCount 目标:保持高命中率(>95%),若过低则需检查缓存配置或更新是否过于频繁。 相关问题: §§相关问题§§ 1. 如何确保Paimon的分区更新后,Flink Lookup Join能及时获取新数据? 2. 当数据量过大无法全量缓存时,应该采用什么策略优化Lookup Join? 3. 在流处理中,如何监控Paimon Lookup Join的性能指标? 4. 如果实时的分区时间不同步,应该如何处理?</think>### Flink 流处理中读取 Paimon 实时关联每日分区的解决方案 在 Flink 流处理中实现实时与每日分区的 Join,需结合 **流动态关联分区** 和 **Lookup Join 优化**。以下是完整实现方案: --- #### 1. 定义与关键配置 ##### (1) 实时流(事实) ```sql CREATE TABLE orders_realtime ( order_id STRING, product_id INT, order_time TIMESTAMP(3), proc_time AS PROCTIME() -- 处理时间属性,用于 Lookup Join ) WITH ( 'connector' = 'paimon', 'path' = 'hdfs:///paimon/orders_realtime', 'scan.mode' = 'latest' -- 实时读取最新数据 ); ``` ##### (2) 每日分区) ```sql CREATE TABLE product_daily ( product_id INT, product_name STRING, price DECIMAL(10, 2), dt STRING -- 分区字段,格式 'yyyy-MM-dd' ) WITH ( 'connector' = 'paimon', 'path' = 'hdfs:///paimon/product_daily', -- 分区配置 -- 'partition.expiration-time' = '7 d', -- 分区过期策略 'partition.timestamp-formatter' = 'yyyy-MM-dd', 'partition.timestamp-pattern' = '$dt', -- Lookup 优化 -- 'lookup.cache-type' = 'FULL', -- 全量缓存每日分区 'lookup.cache-ttl' = '24 h', -- 缓存24小时(每日更新) 'continuous.discovery-interval' = '1 d' -- 每日检查分区更新[^1] ); ``` --- #### 2. 动态分区关联 SQL ```sql SELECT o.order_id, o.product_id, p.product_name, p.price, o.order_time FROM orders_realtime AS o JOIN product_daily FOR SYSTEM_TIME AS OF o.proc_time AS p ON o.product_id = p.product_id AND p.dt = DATE_FORMAT(o.order_time, 'yyyy-MM-dd') -- 动态关联当日分区 ``` --- #### 3. 核心原理与优化 1. **分区路由机制** - 通过 `DATE_FORMAT(o.order_time, 'yyyy-MM-dd')` 动态定位分区路径 - 示例:`order_time = '2023-10-01 10:00:00'` → 关联分区 `dt='2023-10-01'` 2. **缓存策略优化** | **配置项** | **值** | **作用** | |----------------------------|--------------|------------------------------------------| | `lookup.cache-type` | `FULL` | 全量缓存当日分区数据 | | `lookup.cache-ttl` | `24 h` | 每日自动失效旧缓存 | | `continuous.discovery-interval` | `1 d` | 每日自动检测新分区[^1] | 3. **数据一致性保障** - 当新分区(如 `dt=2023-10-02`)写入后: - `continuous.discovery-interval` 触发分区发现 - 新查询自动路由到新分区 - 旧分区缓存按 TTL 自动清除 --- #### 4. 性能调优建议 1. **分区管理** ```sql -- 自动清理历史分区 'partition.expiration-time' = '7 d' ``` 2. **异步加载** ```sql 'lookup.async' = 'true', 'lookup.async-thread-number' = '4' -- 并行加载分区数据 ``` 3. **内存控制** ```sql -- 限制单分区内存占用 'table.exec.resource.default-parallelism' = '8', 'taskmanager.memory.task.off-heap.size' = '512mb' ``` --- #### 5. 监控指标 通过 Flink Web UI 监控: - **分区命中率**:`paimon.dynamic.partition.hit-ratio` - **缓存加载延迟**:`paimon.lookup.load-duration` - **分区更新延迟**:`paimon.partition.discovery-latency` --- > **关键提示**:每日分区需在 **业务低峰期** 批量更新(如凌晨),避免流处理过程中分区切换导致性能波动[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值