Flink读写Hive文档
Flink读写Hive文档
Flink 支持在 BATCH 和 STREAMING 两种模式下从 Hive 读取数据。 当作为 BATCH 应用程序运行时,Flink 将在执行查询的时间点对表的状态执行其查询。 STREAMING 读取将持续监视表并在新数据可用时增量获取。 Flink 会默认读取有界的表。
STREAMING 读取支持使用分区表和非分区表。 对于分区表,Flink 会监控新分区的生成,并在可用时增量读取。 对于非分区表,Flink 会监控文件夹中新文件的生成,并增量读取新文件。
Hive 表还支持 SQL Hints ,比如:
SELECT *
FROM hive_table
/*+
OPTIONS(
'streaming-source.enable'='true',
'streaming-source.consume-start-offset'='2020-05-20'
)
*/;
配置表
Hive 读配置
Key | Default | Type | Description |
---|---|---|---|
streaming-source.enable |
false |
Boolean |
是否开启 streaming source 读取 |
streaming-source.partition.include |
all |
String |
设置读取哪些分区,用来作为 temporal join 时设置,可选项有 all 和 latest 。all 表示读取所有分区;latest 表示按streaming-source.partition.order 的顺序读取最新的分区。 |
streaming-source.monitor-interval |
None |
String |
连续监控分区/文件的时间间隔。 注意:hive 流读取的默认间隔是 '1 m' ,hive streaming temporal join 的默认间隔是'60 m',这是因为有一个框架限制,每个 TM 在当前的 hive streaming temporal join 中都会访问 Hive metaStore 可能会对 metaStore 产生压力的实现,这将在未来改进。 |
streaming-source.partition-order |
partition-name |
String |
读取分区的顺序, 可选项有create-time 、partition-time 、partition-name .create-time 比较分区/文件创建时间,这不是 Hive metaStore 中的分区创建时间,而是文件系统中的文件夹/文件修改时间。partition-time 比较从分区名称中提取的时间。partition-name 比较分区名称的字母顺序。 对于非分区表,此值应始终为 create-time 。 默认情况下,该值为分区名称。 |
streaming-source.consume-start-offset |
None |
String |
从哪里开始读取数据。如何解析和比较偏移量取决于 streaming-source.partition-order 。对于 create-time 和partition-time ,格式是时间戳字符串 (yyyy-[m]m-[d]d [hh:mm:ss])。对于 partition-time ,将使用分区时间提取器从分区中提取时间。对于 partition-name ,是分区名称字符串(例如 'pt_year=2020/pt_mon=10/pt_day=01')。 |
table.exec.hive.fallback-mapred-reader |
false |
Boolean |
如果为false,则使用flink native vectorized reader读取orc文件;如果是true,则使用hadoop mapred record reader读取orc文件。 当满足以下条件时,Flink 将自动使用 Hive 表的map reduce 读取: 1. 数据格式是 ORC 或parquet 。2. 没有复杂数据类型的列,如 hive 类型:List、Map、Struct、Union。 默认情况下启用此功能。 可以设置为 true 以禁用 |
table.exec.hive.infer-source-parallelism |
true |
Boolean |
是否开启自动设置source并行度如。 果为 false,则源的并行度由配置设置。如果为true,则根据拆分数推断source并行度。 |
table.exec.hive.infer-source-parallelism.max |
1000 |
Integer |
自动推断source的最大并行度 |
lookup.join.cache.ttl |
60 min |
Duration |
维表 join 时的缓存 TTL(例如 10 分钟)。 默认情况下,TTL 为 60 分钟。 注意:该选项仅在与有界 hive 表 join时有效,如果是用流的方式 join 请使用 'streaming-source.monitor-interval' 配置数据更新的间隔。 |
NOTE:
- 流式读取不支持 watermark 语法。 这些数据不能作为开窗计算。
Hive 写配置
一. 文件滚动策略
Key | Default | Type | Description |
---|---|---|---|
sink.rolling-policy.file-size |
128MB |
MemorySize |
写HDFS文件滚动策略,文件多大把inprogress文件rename成 part文件 |
sink.rolling-policy.rollover-interval |
30 min |
Duration |
part文件在滚动前可以保持打开的最长时间(默认为 30 分钟,以避免出现许多小文件)。检查频率由sink.rolling-policy.check-interval 选项控制。 |
sink.rolling-policy.check-interval |
1 min |
Duration |
以此值来生成一个定时器,定时检查是否满足sink.rolling-policy.rollover-interval 的条件。注意这个不是用于检查非活跃文件的配置,sql里没有检查非活跃文件的配置 |
NOTE: 对于bulk(桶) 格式的数据(parquet, orc, avro),文件滚动由 checkpoint 的时间间隔控制,但在一个 checkpoint 内,也会受以上参数控制。也就是说每次 checkpoint 必定会把 inprogress 文件变成 part 文件,不管有没有满足以上策略。
NOTE: 对于按行格式写入的数据(csv,json),在没有设置自动压缩小文件的配置的情况下,文件滚动完全由以上策略控制。也就是说这种数据格式的 inprogress 文件可能跨越了多个 checkpoint 周期,在这种情况下恢复任务,如果 hadoop 版本小于 2.7, 会报Flink Truncation is not available in hadoop version < 2.7 , You are on Hadoo
的错误,因为 hadoop2.7之前的版本不支持 truncate 。
二. 小文件合并设置
Key | Default | Type | Description |
---|---|---|---|
auto-compaction |
false |
Boolean |
是否开启小文件合并。 数据将写入临时文件。 checkpoint 完成后,临时文件将被压缩。 临时文件在压缩前是不可见的。如果启用,文件压缩将根据目标文件大小将多个小文件合并为更大的文件。 |
compaction.file-size |
(none) | MemorySize |
压缩文件的大小,默认为sink.rolling-policy.file-size 设置的大小. |
NOTE:
- 仅压缩单个 checkpoint 中的文件,即至少生成与 checkpoint 数量相同的文件。
- 合并前的文件是不可见的,所以文件的时效性是:checkpoint 间隔 + 压缩时间。
- 如果压缩时间过长,作业会产生背压并增加 checkpoint 的时间。
三. 分区提交设置
写入分区文件后,通常需要通知下游应用程序。 例如,将分区添加到 Hive metastore
或在目录中写入 _SUCCESS
文件。 Flink内置了分区提交功能,用户也可以自定义策略分区提交。分区提交主要包括 triggers
和policies
1. 分区提交trigger
配置
Key | Default | Type | Description |
---|---|---|---|
sink.partition-commit.trigger |
process-time |
String |
分区提交触发类型:process-time :基于机器时间,既不需要分区时间提取,也不需要 watermark 生成。 “当前系统时间”大于“分区创建系统时间” + “延迟(sink.partition-commit.delay )”,就提交分区。 partition-time :基于分区时间,从分区值中提取,需要生成 watermark。 watermark 大于 “从分区值中提取的时间” + “延迟(sink.partition-commit.delay )”,就提交分区。 |
sink.partition-commit.delay |
0 s |
Duration |
分区提交延迟。 如果是以天分区,应该是'1 d',如果是以小时分区,应该是'1 h'。 |
sink.partition-commit.watermark-time-zone |
UTC |
String |
设置 watermark 的时区,会根据这个时区把 watermark (long类型的时间戳)转换为 TIMESTAMP , 用于与分区时间进行比较来决定分区是否提交。仅在 sink.partition-commit.trigger 设置为 partition-time 时生效。 如果此选项配置不正确,例如 source rowtime 定义成 TIMESTAMP_LTZ (local time zone),但未配置此配置,用户可能会在几个小时后看到提交的分区。 默认值为UTC ,表示 watermark 在 TIMESTAMP 类型的列上定义或未定义。 如果在 TIMESTAMP_LTZ 类型的列上定义了watermark,则watermark的时区为会话时区。 选项值可以是全名(例如“America/Los_Angeles”)或自定义时区 ID(例如“GMT-08:00”)。 |
process-time
和partition-time
:
process-time
. 不需要提取分区值也不需要 watermark。分区提交根据当前时间和分区创建时间来定。这种是比较通用的触发器,但数据不够准确,可能会发生数据漂移。partition-time
.根据分区时间和 watermark 来触发分区提交. 需要 watermark 生成和分区提取。
如果你只想让下游立马看到数据,不管数据是否已经完成,可以设置成:
- 'sink.partition-commit.trigger'='process-time'
- 'sink.partition-commit.delay'='0s' (Default value) Once there is data in the partition, it will immediately commit. Note: the partition may be committed multiple times.
如果想数据完成时(假设以小时作为分区)才让下游看到数据,并且 source 有 watermark,可以设置成:
- 'sink.partition-commit.trigger'='partition-time'
- 'sink.partition-commit.delay'='1h'
NOTE:
- 迟到数据的处理:当一条记录应该写入一个已经提交的分区时,该记录就会被写入到它对应的分区中,然后再次触发该分区的提交。
- 分区提交其实是跟着 checkpoint 走的,分区提交是在 writer 之后的一个算子,writer 会在 checkpoint 时,把分区信息传给下游的分区提交的 commiter 算子。
2. 分区提取器
Key | Default | Type | Description |
---|---|---|---|
partition.time-extractor.kind |
default |
String |
支持default 和custom 。 default 类型,需要配置partition.time-extractor.timestamp-pattern 。 custom 类型,需要指定partition.time-extractor.class 。 |
partition.time-extractor.class |
none |
String |
分区提取类,需要实现PartitionTimeExtractor 接口 |
partition.time-extractor.timestamp-pattern |
none |
String |
default 方式支持来自第一个字段的 'yyyy-MM-dd HH:mm:ss'。 如果只从单个分区字段'dt'中提取,可以配置:'\(dt'。 <br/>如果多个分区字段中提取,比如'year'、'month'、'day'和'hour',可以配置:'\)year-\(month-\)day \(hour:00:00'。<br/>如果从两个分区字段'dt'和'hour'中提取,可以配置:'\)dt $hour:00:00'。 |
3. 分区提交策略
分区提交策略指的是分区提交后做的操作
- 第一种是写到 metastore, 只有 hive 表支持 metastore policy, file system 通过目录结构管理分区。
- 第二种是生成 success file, 在分区对应的目录中写入一个空文件。
Key | Default | Type | Description |
---|---|---|---|
sink.partition-commit.policy.kind |
(none) |
String |
提交分区的策略是通知下游应用该分区已完成写入,该分区已准备好被读取。可选值为 metastore 和 success-file,也可以同时设置 'metastore,success-file'metastore: 把分区加到 hive metastore。 只有 hive 表支持这种策略。success-file: 生成一个 '_success' 空文件。custom : 自定义实现。 |
sink.partition-commit.policy.class |
(none) |
String |
自定义的分区提交策略类,需要实现PartitionCommitPolicy 接口 |
sink.partition-commit.success-file.name |
_SUCCESS |
String |
success-file 策略下,生成的文件名,默认是_SUCCESS 。 |
可以自定义提交分区策略,如下:
public class AnalysisCommitPolicy implements PartitionCommitPolicy {
private HiveShell hiveShell;
@Override
public void commit(Context context) throws Exception {
if (hiveShell == null) {
hiveShell = createHiveShell(context.catalogName());
}
hiveShell.execute(String.format(
"ALTER TABLE %s ADD IF NOT EXISTS PARTITION (%s = '%s') location '%s'",
context.tableName(),
context.partitionKeys().get(0),
context.partitionValues().get(0),
context.partitionPath()));
hiveShell.execute(String.format(
"ANALYZE TABLE %s PARTITION (%s = '%s') COMPUTE STATISTICS FOR COLUMNS",
context.tableName(),
context.partitionKeys().get(0),
context.partitionValues().get(0)));
}
}
四. Sink 并行度
Sink的并行度,批和流都支持。 默认情况下,并行度配置为其上游的并行度。 当配置了不同于上游并行度的并行度时,写入文件和压缩文件(如果使用)将使用该值并行度。
Key | Default | Type | Description |
---|---|---|---|
sink.parallelism |
(none) |
Integer |
完整例子
一. 维表关联
1. Temporal Join 最新分区
对于随时间变化的分区表,我们可以将其作为无界流读出,如果每个分区都包含一个版本的完整数据,则该分区可以充当时态表的一个版本,时态表的版本保留数据 的分区。
Flink 支持 processing time 的 temporal join,自动跟踪临时表的最新分区(版本),最新分区(版本)由 'streaming-source.partition-order' 选项定义。
NOTE:此功能仅在 STREAMING 模式下支持。
CREATE TABLE dimension_table (
product_id STRING,
product_name STRING,
unit_price DECIMAL(10, 4),
pv_count BIGINT,
like_count BIGINT,
comment_count BIGINT,
update_time TIMESTAMP(3),
update_user STRING,
...
) PARTITIONED BY (pt_year STRING, pt_month STRING, pt_day STRING) TBLPROPERTIES (
-- 这些属性也可以通过 SQL Hits 动态指定
-- 用 partition-name 的顺序加载最新分区, 每12h刷新一次 (官方推荐)
'streaming-source.enable' = 'true',
'streaming-source.partition.include' = 'latest',
'streaming-source.monitor-interval' = '12 h',
'streaming-source.partition-order' = 'partition-name'
-- 用分区文件的创建时间 create-time 的顺序加载最新分区, 每12h刷新一次 (官方推荐)
'streaming-source.enable' = 'true',
'streaming-source.partition.include' = 'latest',
'streaming-source.monitor-interval' = '12 h',
'streaming-source.partition-order' = 'create-time'
-- 用分区时间 partition-time 的顺序加载最新分区, 每12h刷新一次 (官方推荐)
'streaming-source.enable' = 'true',
'streaming-source.partition.include' = 'latest',
'streaming-source.monitor-interval' = '12 h',
'streaming-source.partition-order' = 'partition-time',
'partition.time-extractor.kind' = 'default',
'partition.time-extractor.timestamp-pattern' = '$pt_year-$pt_month-$pt_day 00:00:00'
);
CREATE TABLE orders_table (
order_id STRING,
order_amount DOUBLE,
product_id STRING,
log_ts TIMESTAMP(3),
proctime as PROCTIME()
) WITH (...);
SELECT * FROM orders_table AS o
JOIN dimension_table FOR SYSTEM_TIME AS OF o.proctime AS dim
ON o.product_id = dim.product_id;
2. Temporal Join 最新表数据
对于 Hive 表,我们也可以将其作为有界流读出。 在这种情况下,Hive 表只能在我们查询时跟踪其最新版本。 最新版本的表保留了 Hive 表的所有数据。
使用最新版本表的 Temporal Join 时,Hive 表数据将缓存在 Slot 内存中。 不需要任何额外的配置。 或者,您可以使用以下属性配置 Hive 表缓存的 TTL。 缓存过期后,会再次扫描 Hive 表以加载最新数据。
SET table.sql-dialect=hive;
CREATE TABLE dimension_table (
product_id STRING,
product_name STRING,
unit_price DECIMAL(10, 4),
pv_count BIGINT,
like_count BIGINT,
comment_count BIGINT,
update_time TIMESTAMP(3),
update_user STRING,
...
) TBLPROPERTIES (
'streaming-source.enable' = 'false',
'streaming-source.partition.include' = 'all',
'lookup.join.cache.ttl' = '12 h'
);
SET table.sql-dialect=default;
CREATE TABLE orders_table (
order_id STRING,
order_amount DOUBLE,
product_id STRING,
log_ts TIMESTAMP(3),
proctime as PROCTIME()
) WITH (...);
SELECT * FROM orders_table AS o
JOIN dimension_table FOR SYSTEM_TIME AS OF o.proctime AS dim
ON o.product_id = dim.product_id;
Note:
- 每个 join 的subtask 都需要保留自己的 Hive 表缓存。 请确保 TM 有足够的内存。
- 建议为streaming-source.monitor-interval 或 lookup.join.cache.ttl 设置一个相对较大的值。 否则,作业很容易出现性能问题,因为表需要过于频繁地更新和重新加载。
- 目前只是在缓存需要刷新时加载整个 Hive 表。 因为没有办法区分新数据和旧数据。
二. 写 Hive 表
Flink 支持 BATCH 和 STREAMING 两种模式的Hive表写入。 当作为 BATCH 应用程序运行时,Flink 将写入 Hive 表,仅在作业完成时使这些记录可见。 BATCH 写入支持追加和覆盖现有表。
INSERT INTO mytable SELECT 'Tom', 25;
INSERT OVERWRITE mytable SELECT 'Tom', 25;
数据也可以插入到特定的分区中。
# ------ 写入静态分区 ------
INSERT OVERWRITE myparttable PARTITION (my_type='type_1', my_date='2019-08-08') SELECT 'Tom', 25;
# ------ 写入动态分区 ------
INSERT OVERWRITE myparttable SELECT 'Tom', 25, 'type_1', '2019-08-08';
# ------ 静态和动态混合写入 ------
INSERT OVERWRITE myparttable PARTITION (my_type='type_1') SELECT 'Tom', 25, '2019-08-08';
STREAMING 不断地向 Hive 添加新数据。 用户可以动态的使用属性来控制提交行为。 STREAMING 不支持INSERT OVERWRITE
CREATE TABLE hive_table (
user_id STRING,
order_amount DOUBLE
) PARTITIONED BY (dt STRING, hr STRING) STORED AS parquet TBLPROPERTIES (
'partition.time-extractor.timestamp-pattern'='$dt $hr:00:00',
'sink.partition-commit.trigger'='partition-time',
'sink.partition-commit.delay'='1 h',
'sink.partition-commit.policy.kind'='metastore,success-file'
);
CREATE TABLE kafka_table (
user_id STRING,
order_amount DOUBLE,
log_ts TIMESTAMP(3),
WATERMARK FOR log_ts AS log_ts - INTERVAL '5' SECOND
) WITH (...);
-- stream sql, insert into hive table
INSERT INTO TABLE hive_table
SELECT user_id, order_amount, DATE_FORMAT(log_ts, 'yyyy-MM-dd'), DATE_FORMAT(log_ts, 'HH')
FROM kafka_table;
-- batch sql, select with partition pruning
SELECT * FROM hive_table WHERE dt='2020-05-20' and hr='12';
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步