Flink-Hudi技术实践:Upsert场景开发实践

0 阅读8分钟

一、背景介绍

之前写了一篇Flink实时场景下Hudi-Insert开发实践的文章,主要讲述纯插入操作场景下的技术原理与配置实践。然而,Upsert能力才是Hudi实现数据湖准实时数据更新的一大利器,Hudi凭借其Upsert(Update+Insert)能力,高效处理数据的插入、更新与删除操作,并保证基于主键的数据唯一性和一致性。今天聚焦在Hudi-Upsert操作场景,介绍Upsert模式的的技术原理、适用场景、关键参数与最佳实践,提供一套可落地的开发实践参考。

二、Hudi-Upsert技术特性与适用场景

1.Hudi-Upsert技术特性

Hudi的upsert模式是 Hudi-Flink 写入的核心模式,其核心逻辑为:将流式数据的插入、更新、删除操作统一写入 Hudi MOR 表,基于 主键(分区表需结合分区字段)的索引机制,自动将写入行为标记为update(主键已存在)或insert(主键不存在),保障表中数据的唯一性与一致性;同时支持基于precombine.field字段解决同主键数据冲突,基于hoodie.is_deleted字段实现物理删除。

  • 增量写入:Flink 将 CDC 流数据写入 Hudi MOR 表的增量日志文件(Log File),保证秒级低延迟写入。
  • 主键去重与冲突解决:基于「主键 + 分区字段」判断数据唯一性,若存在同主键数据,通过precombine.field指定的时间 / 版本字段排序,自动保留最新记录。
  • 物理删除支持:通过hoodie.is_deleted字段标记删除记录,实现数据的物理删除(而非逻辑标记)。
  • Compaction 合并:定期将 Log 文件合并到 Parquet 基础数据文件,生成新版本的基础文件,解决 Log 文件堆积问题,平衡读写性能。

2.Hudi-Upsert应用场景

基于Upsert模式的技术特性,其典型应用场景包括:

  1. 数据库变更流捕获(CDC)
    从MySQL Binlog或Flink CDC流中获取的数据,包含大量INSERT、UPDATE、DELETE操作。使用Upsert模式写入Hudi MOR表,可确保Hudi表中始终保留每条记录的最新状态,且不会出现重复数据。这正是构建实时ODS层的核心方案。
  2. 实时数据同步与ETL
    在数仓分层建设中,ODS层到DWD层的处理往往需要保留数据的变更历史。开启changelog.enabled参数后,Hudi MOR表可保留消息的所有变更(I/-U/U/D),下游Flink作业可进行全链路的增量计算。
  3. 数据湖上的近实时查询
    对于需要兼顾写入实时性与查询性能的场景,MOR表的Upsert模式提供了理想的平衡——写入时仅追加Log文件,查询时通过Compaction合并成Parquet,实现分钟级数据可见。

三、Hudi-Upsert参数配置指南

1.Flink-Hudi表基础with参数

'connector' = 'hudi'
'table.type' = 'MERGE_ON_READ'
'path' = ''
'write.operation' = 'upsert'
'hoodie.datasource.write.recordkey.field' = ''
'hoodie.datasource.write.partitionpath.field' = ''
'metadata.enabled' = 'false'
'hive_sync.enable' = 'true'
'hive_sync.db' = ''
'hive_sync.table' = ''
'hive_sync.mode' = 'hms'
'hive_sync.metastore.uris' = ''
'hive_sync.table.strategy' = 'RT'
'hoodie.datasource.write.hive_style_partitioning' = 'true'
'index.type' = 'BUCKET'
'hoodie.bucket.index.num.buckets' = ''
'write.precombine' = 'true'
'precombine.field' = ''
'write.rate.limit' = ''

其他自定义参数可参阅Hudi官网(hudi.apache.org/docs/1.0.2/…

2.Compaction介绍

Compaction 是 Hudi MOR 表的独有核心机制,其核心逻辑是:定期将 MOR 表中的增量日志文件(Log File)合并到对应的Parquet 基础数据文件,生成新版本的基础数据文件,解决 Log 文件过多导致的查询性能下降问题,平衡写入延迟与查询性能。Compaction 分为两个独立阶段:

  • 计划生成:由 Flink 写入任务实时生成合并计划(必须配置compaction.schedule.enabled='true');
  • 计划执行:分为在线合并(Flink 任务内异步执行)和离线合并(独立 Spark 任务执行)两种方式,二选一。

其中Compaction核心参数说明如下:

参数名含义默认值详细说明
compaction.async.enabled是否启用异步合并(MOR 表专属)true设为true在线合并模式false离线合并模式
compaction.schedule.enabled是否启用生成合并计划(MOR 表专属)true在线 / 离线合并均需设置为true,由 Flink 任务实时生成合并计划
compaction.trigger.strategy合并触发策略num_or_time支持按 commit 次数(num_commits)或时间(time)触发合并
compaction.delta_commits多少次 delta commit 生成合并计划5离线合并需与 checkpoint 间隔配合,至少 20-30 分钟生成一次计划,推荐配置 10-15
compaction.delta_seconds多少秒触发 compaction3600离线合并需与 checkpoint 间隔配合,至少 20-30 分钟生成一次计划
compaction.max_memory合并可用内存(MB,超出则溢写)100直接影响 log 文件合并效率,推荐不低于 300MB;数据量越大,需增大该值提升合并效率
write.task.max.size单个 write task 最大内存(MB)1024单桶 1GB 的 Parquet 文件合并耗时约 5min,该值与compaction.max_memory共同决定合并性能

根据内部实践,内存设置一般存在以下关系:compaction.max_memory < write.task.max.size < taskmanager堆内存(约taskmanager总内存40%)。

3.在线/离线Compaction实践

为保障Hudi表数据查询性能,我们以20min内完成Compaction为目标,在内部展开多场景压测验证(数据大小、字段数、TPS、资源规格等),最后制定了一个Compaction策略选型的参考原则:

  • checkpoint 间隔 120s
  • 单条数据大小 0.2kb
  • 表字段数 60
  • parquet 文件大小推荐 0.3g~1g,最大不超过 2g
存量数据量在线合并适用 TPS离线合并适用 TPS
0-1000w≤4000>4000
1000w-2000w≤3000>3000
≥2000w或TPS>4000不推荐推荐
  • 存量数据量≤1000w(在线合并)
TPS 区间TM 资源合并和写入配置
0-10004c12g'compaction.max_memory'='300', ``'write.task.max.size'='1200',``'write.rate.limit'='1200'
1000-25006c18g'compaction.max_memory'='500', ``'write.task.max.size'='1800',``'write.rate.limit'='3000'
2500-40008c24g'compaction.max_memory'='500', ``'write.task.max.size'='1800',``'write.rate.limit'='6000'
  • 存量数据量 1000w-2000w(在线合并)
TPS 区间TM 资源合并和写入配置
0-10004c12g'compaction.max_memory'='300', ``'write.task.max.size'='1200',``'write.rate.limit'='1200'
1000-20006c18g'compaction.max_memory'='500', ``'write.task.max.size'='1800',``'write.rate.limit'='3000'
2000-30008c24g'compaction.max_memory'='500', ``'write.task.max.size'='1800',``'write.rate.limit'='5000'
  • 存量数据量>2000w或TPS>4000(离线合并)
TPS 区间TM 资源合并和写入配置
0-50004c12g'compaction.max_memory'='100', ``'write.task.max.size'='1200',``'write.rate.limit'='7000'
5000-80006c18g'compaction.max_memory'='100', ``'write.task.max.size'='1900',``'write.rate.limit'='10000'
8000-120008c24g'compaction.max_memory'='100', ``'write.task.max.size'='1900',``'write.rate.limit'='13000'

四、总结展望   

本文围绕Flink写入Hudi MOR表的Upsert模式,系统介绍了其技术原理、应用场景以及核心的Compaction配置实践。技术没有银弹,通过深入理解原理、结合场景精准调优,我们能够最大化发挥Flink-Hudi-Upsert组合的潜力,为实时数据湖建设提供坚实支撑。

五、附录(Flink-Hudi Upsert 与 Insert 多维对比)

对比维度Flink-Hudi Upsert-MORFlink-Hudi Insert-MOR核心选型结论
写入核心逻辑主键匹配,支持增 / 改 / 删,自动去重、保留最新版本纯追加写入,无主键匹配、无去重,允许主键重复有更新 / 删除 / 保最新 → Upsert;纯日志追加 → Insert
索引依赖必须维护主键索引(Bucket/Bloom),索引开销高不依赖主键索引,无索引查询 & 写入开销高吞吐日志场景优先 Insert,节省索引资源
冲突处理依赖precombine.field自动覆盖旧数据,解决乱序不处理冲突,多条同主键数据全部留存需要数据收敛→Upsert;需要全链路溯源→Insert
删除能力原生支持物理 / 逻辑删除 CDC 数据不支持删除标记,删除数据会变成新增记录CDC 业务、订单状态变更必选 Upsert
后台治理机制依赖Compaction合并日志,解决增量更新堆积依赖Clustering聚类,解决小文件碎片合并目的不同:Upsart 收敛数据,Insert 规整文件
性能开销写入延迟中等,高 TPS 下易受 Compaction 抢占资源写入延迟极低,峰值吞吐远高于 Upsert亿级日志埋点、IoT 时序数据首选 Insert
适用数据体量中低~高 TPS,存量大、频繁更新的业务数据超高 TPS,不可变、仅追加的流水数据60 字段:Upsert 建议≤4000TPS,Insert 可上万 TPS
运维复杂度需区分在线 / 离线 Compaction,关注数据一致性只需治理小文件 Clustering,逻辑简单轻量化运维、快速上线优先 Insert
典型业务场景MySQL CDC 入湖、实时维表、订单交易、用户画像APP 埋点、行为日志、系统监控、IoT 设备数据业界标准选型,严格匹配业务数据特征