1. HiveModule:把 Hive 内置函数当成 Flink 系统函数用
1.1 作用是什么
HiveModule 会把 Hive 内置函数注册成 Flink 的 system (built-in) functions。你在 Flink SQL/Table API 里能直接调用 Hive 的函数(包含大量字符串、日期、数学、条件判断等)。
1.2 Java 侧加载方式
核心就是 loadModule:
java
String name = "myhive";
String version = "2.3.4";
tableEnv.loadModule(name, new HiveModule(version));
注意点:Hive 不同版本 built-in function 的行为可能略有差异,生产上建议和你 metastore / hive-exec 的版本对齐。
1.3 线程安全提醒(非常关键)
文档里明确提到:部分旧版本 Hive 内置函数存在线程安全问题 。
Flink 是并行执行(多线程/多 Task),如果你用了有线程安全问题的函数,很容易出现"偶发错结果/偶发 NPE/偶发脏状态"。
实践建议:
- 优先选较新的 Hive 版本(在你集群兼容范围内)
- 或者对 Hive 打补丁(很多公司都会在 hive-exec 上做 patch)
2. 性能加速:Native Hive Aggregate Functions(hash 聚合)
2.1 为什么会变快
当 HiveModule 的优先级高于 CoreModule 时,Flink 会优先使用 Hive built-in 函数。问题在于:
Hive built-in 聚合函数在 Flink 里只能用 sort-based aggregation(排序聚合),在大数据量场景通常会更慢、CPU 更吃紧。
从 Flink 1.17 开始引入 "native hive aggregation functions":
让 sum/count/avg/min/max 这些聚合可以走 hash-based aggregation(哈希聚合),通常能带来明显性能收益(尤其是 group by 场景)。
2.2 如何开启
开关是 job-level option:
table.exec.hive.native-agg-function.enabled = true
开启后,sum/count/avg/min/max 会优先走原生实现(前提:类型支持、planner 能匹配)。
2.3 两个现实限制(别踩坑)
1)能力不完全对齐 Hive built-in
比如某些数据类型不支持,或边界行为略不同。
建议:如果性能不是瓶颈,不必强开;要开就做对账验证。
2)SqlClient 里"每个作业单独开关"暂时不够友好
文档提到目前 SqlClient 场景下无法对单个 job 精细开关,只支持"模块级"开启:
先开启 option,再 load HiveModule(这个后续版本会改善,但现在先按这个来)
3. 复用 Hive UDF/GenericUDF/UDTF/UDAF:不用重写,Flink 自动翻译
3.1 支持哪些类型
Flink 可以直接使用 Hive 里已有的这些函数类型:
- UDF
- GenericUDF
- GenericUDTF
- UDAF
- GenericUDAFResolver2
Flink 在 planner/executor 里会自动转换:
- Hive UDF / GenericUDF → Flink ScalarFunction
- Hive GenericUDTF → Flink TableFunction
- Hive UDAF / GenericUDAFResolver2 → Flink AggregateFunction
这点非常实用:你历史 Hive SQL 的函数资产可以直接迁移到 Flink SQL 体系里。
3.2 使用 Hive UDF 的前置条件(缺一不可)
要在 Flink 里用 Hive UDF,你必须满足三件事:
1)会话当前 catalog 指向一个 HiveCatalog (且该 HMS 里注册了函数)
2)Flink 的 classpath 里能找到这个函数的 jar(放 Flink /lib 或 SQL Client -l/-C)
3)在 SQL 里按 Hive Metastore 注册的函数名直接调用
3.3 典型例子:UDF + GenericUDF + UDTF(LATERAL TABLE)
你贴的例子是典型组合:先用 UDF 处理字段,再用 GenericUDF 包一层,再用 UDTF 做拆分扩行。
实战写法一般长这样(示意):
sql
SELECT
mygenericudf(myudf(name), 1) AS a,
mygenericudf(myudf(age), 1) AS b,
t.col1
FROM mysourcetable
, LATERAL TABLE(mygenericudtf(name, 1)) AS t(col1);
这里的要点:
LATERAL TABLE(...)是把 UDTF 的多行输出"扩"到结果集中AS t(col1)给 UDTF 输出列取别名(否则字段名不好用)- 常量参数(如 1)在 GenericUDF/UDTF 里经常会校验为 ConstantObjectInspector,这也是 Hive 体系的常见模式
4. 生产实践建议:怎么把"兼容性"和"性能收益"都拿到
4.1 最小闭环验证(强烈建议)
上线前建议搞两组 SQL:
- 正确性验证 SQL:小样本 + Print Sink,把聚合/维度 join/UDTF 扩行结果打印出来核对
- 性能压测 SQL:同样逻辑改 BlackHole Sink,把外部 IO 干扰去掉,专注看算子耗时与反压
这样你就能快速判断:
- 是否是 Hive 函数/UDTF 造成算子放大、扩行过猛
- 是否 native agg 开启后 group by 明显降 CPU / 降延迟
- 是否瓶颈在 sort-based agg(没匹配到 native)还是在下游 sink
4.2 Native agg 什么时候值得开
- 大量 group by 聚合(尤其宽表、聚合维度多)
- checkpoint 周期内 CPU 偏高、sort/merge 相关算子耗时明显
- 你用的聚合函数主要就是 sum/count/avg/min/max,且类型在支持范围内
4.3 Hive UDTF 迁移常见坑
- 扩行倍数大(例如 split 后每个 token 输出多行),吞吐会线性下降
- UDTF + join 组合时可能导致数据量暴涨,先在 Print 场景估算扩行倍率
- 并行度高时,函数内部若有非线程安全静态状态,极容易出问题(尤其老 Hive 代码)