Flink笔记

flink特性:

# Table
Table 可以是虚拟的(视图 VIEWS)也可以是常规的(表 TABLES)。 表TABLES描述的是外部数据,例如文件、数据库表或者消息队列。

临时表(Temporary Table)和永久表(Permanent Table) #
表可以是临时的,并与单个 Flink 会话(session)的生命周期相关,也可以是永久的,并且在多个 Flink 会话和群集(cluster)中可见。

永久表需要 catalog(例如 Hive Metastore)以维护表的元数据。一旦永久表被创建,它将对任何连接到 catalog 的 Flink 会话可见且持续存在,直至被明确删除。
临时表通常保存于内存中并且仅在创建它们的 Flink 会话持续期间存在。这些表对于其它会话是不可见的。它们不与任何 catalog 或者数据库绑定但可以在一个命名空间(namespace)中创建。即使它们对应的数据库被删除,临时表也不会被删除。
屏蔽(Shadowing):可以使用与已存在的永久表相同的标识符去注册临时表。临时表会屏蔽永久表,并且只要临时表存在,永久表就无法访问。所有使用该标识符的查询都将作用于临时表。

表总是通过三元标识符注册,包括 catalog 名、数据库名和表名。

# 翻译与执行查询 #
不论输入数据源是流式的还是批式的,Table API 和 SQL 查询都会被转换成 DataStream 程序。 查询在内部表示为逻辑查询计划,并被翻译成两个阶段:
> 优化逻辑执行计划
> 翻译成 DataStream 程序

# 状态管理#状态使用
形如 SELECT ... FROM ... WHERE 这种只包含字段映射或过滤器的查询的查询语句通常是无状态的管道。 然而诸如 join、 聚合或去重操作需要在 Flink 抽象的容错存储内保存中间结果。

# 处理时间
user_action_time AS PROCTIME() -- 声明一个额外的列作为处理时间属性
函数 PROCTIME() 的返回类型是 TIMESTAMP_LTZ
SELECT TUMBLE_START(user_action_time, INTERVAL '10' MINUTE), COUNT(DISTINCT user_name)
FROM user_actions
GROUP BY TUMBLE(user_action_time, INTERVAL '10' MINUTE);

# 时态表(Temporal Tables) #
时态表(Temporal Table)是一张随时间变化的表 – 在 Flink 中称为动态表,时态表中的每条记录都关联了一个或多个时间段,所有的 Flink 表都是时态的(动态的)。
根据时态表是否可以追踪自身的历史版本与否,时态表可以分为 版本表 和 普通表
在 Flink 中,定义了主键约束和事件时间属性的表就是版本表。

-- 定义一张版本表
CREATE TABLE product_changelog (
product_id STRING,
product_name STRING,
product_price DECIMAL(10, 4),
update_time TIMESTAMP(3) METADATA FROM 'value.source.timestamp' VIRTUAL,
PRIMARY KEY(product_id) NOT ENFORCED, -- (1) 定义主键约束
WATERMARK FOR update_time AS update_time -- (2) 通过 watermark 定义事件时间
) WITH (
'connector' = 'kafka',
'topic' = 'products',
'scan.startup.mode' = 'earliest-offset',
'properties.bootstrap.servers' = 'localhost:9092',
'value.format' = 'debezium-json'
);
注意: METADATA FROM 'value.source.timestamp' VIRTUAL 语法的意思是从每条 changelog 中抽取 changelog 对应的数据库表中操作的执行时间,强烈推荐使用数据库表中操作的 执行时间作为事件时间 ,否则通过时间抽取的版本可能和数据库中的版本不匹配。


查询操作还可以在 VALUES 子句中使用内联数据。每一个元组对应一行,另外可以通过设置别名来为每一列指定名称。
SELECT order_id, price FROM (VALUES (1, 2.0), (2, 3.1)) AS t (order_id, price)

# CREATE 语句
# Metadata Columns
CREATE TABLE MyTable (
`user_id` BIGINT,
`name` STRING,
`record_time` TIMESTAMP_LTZ(3) METADATA FROM 'timestamp' -- reads and writes a Kafka record's timestamp
`timestamp` TIMESTAMP_LTZ(3) METADATA -- use column name as metadata key
`timestamp` BIGINT METADATA -- cast the timestamp as BIGINT
) WITH (
'connector' = 'kafka'
...
);

# Computed Columns
CREATE TABLE MyTable (
`user_id` BIGINT,
`price` DOUBLE,
`quantity` DOUBLE,
`cost` AS price * quanitity, -- evaluate expression and supply the result to queries
) WITH (
'connector' = 'kafka'
...
);
INSERT INTO MyTable SELECT user_id, name, record_time + INTERVAL '1' SECOND FROM MyTable;

# Flink 流处理应用中常见的需求/方案是什么
Windows -----> Watermark -----> allowLateNess -----> sideOutPut
用Windows把流数据分块处理,用Watermark确定什么时候不再等待更早的数据/触发窗口进行计算,用allowLateNess 将窗口关闭时间再延迟一段时间。用sideOutPut 最后兜底把数据导出到其他地方。

# WATERMARK
https://www.cnblogs.com/rossiXYZ/p/12286407.html
Watermark是Apache Flink为了处理EventTime 窗口计算提出的一种机制,本质上也是一种时间戳。watermark是用于处理乱序事件或延迟数据的,这通常用watermark机制结合window来实现(Watermarks用来触发window窗口计算)。
比如对于late element,我们不能无限期的等下去,必须要有个机制来保证一个特定的时间后,必须触发window去进行计算了。这个特别的机制,就是watermark。 可以把Watermark看作是一种告诉Flink一个消息延迟多少的方式。定义了什么时候不再等待更早的数据。

WATERMARK 定义了表的事件时间属性,其形式为 WATERMARK FOR rowtime_column_name AS watermark_strategy_expression 。
rowtime_column_name 把一个现有的列定义为一个为表标记事件时间的属性。该列的类型必须为 TIMESTAMP(3),且是 schema 中的顶层列,它也可以是一个计算列。
watermark_strategy_expression 定义了 watermark 的生成策略。它允许使用包括计算列在内的任意非查询表达式来计算 watermark ;表达式的返回类型必须是 TIMESTAMP(3),表示了从 Epoch 以来的经过的时间。 返回的 watermark 只有当其不为空且其值大于之前发出的本地 watermark 时才会被发出(以保证 watermark 递增)。每条记录的 watermark 生成表达式计算都会由框架完成。 框架会定期发出所生成的最大的 watermark ,如果当前 watermark 仍然与前一个 watermark 相同、为空、或返回的 watermark 的值小于最后一个发出的 watermark ,则新的 watermark 不会被发出。 Watermark 根据 pipeline.auto-watermark-interval 中所配置的间隔发出。 若 watermark 的间隔是 0ms ,那么每条记录都会产生一个 watermark,且 watermark 会在不为空并大于上一个发出的 watermark 时发出。
使用事件时间语义时,表必须包含事件时间属性和 watermark 策略。

Flink 提供了几种常用的 watermark 策略。
严格递增时间戳: WATERMARK FOR rowtime_column AS rowtime_column。
发出到目前为止已观察到的最大时间戳的 watermark ,时间戳大于最大时间戳的行被认为没有迟到。
递增时间戳: WATERMARK FOR rowtime_column AS rowtime_column - INTERVAL '0.001' SECOND。
发出到目前为止已观察到的最大时间戳减 1 的 watermark ,时间戳大于或等于最大时间戳的行被认为没有迟到。
有界乱序时间戳: WATERMARK FOR rowtime_column AS rowtime_column - INTERVAL 'string' timeUnit。
发出到目前为止已观察到的最大时间戳减去指定延迟的 watermark ,例如, WATERMARK FOR rowtime_column AS rowtime_column - INTERVAL '5' SECOND 是一个 5 秒延迟的 watermark 策略。

CREATE TABLE Orders (
`user` BIGINT,
product STRING,
order_time TIMESTAMP(3),
WATERMARK FOR order_time AS order_time - INTERVAL '5' SECOND
) WITH ( . . . );

# 基于Event Time的事件处理,Flink默认的事件触发条件为:
对于out-of-order及正常的数据而言:
> watermark的时间戳 > = window endTime
> 在 [window_start_time,window_end_time] 中有数据存在。

对于late element太多的数据而言:
> Event Time > watermark的时间戳


WaterMark时间可以用Flink系统现实时间,也可以用处理数据所携带的Event time。
使用Flink系统现实时间,在并行和多线程中需要注意的问题较少,因为都是以现实时间为标准。
如果使用处理数据所携带的Event time作为WaterMark时间,需要注意两点:
> 因为数据到达并不是循序的,注意保存一个当前最大时间戳作为WaterMark时间
> 并行同步问题

总结:
> 窗口window 的作用是为了周期性的获取数据。
> watermark的作用是防止数据出现乱序(经常),事件时间内获取不到指定的全部数据,而做的一种保险方法。
> allowLateNess是将窗口关闭时间再延迟一段时间。
> sideOutPut是最后兜底操作,所有过期延迟数据,指定窗口已经彻底关闭了,就会把数据放到侧输出流。

例子
例如基于Event Time的数据,自身都包含一个类型为timestamp的字段,假设叫做rowtime,例如1543903383(2018-12-04 14:03:03),定义一个基于rowtime列,策略为偏移3s的watermark,这条数据的水位线时间戳则是:
1543903383-3000 = 1543900383(2018-12-04 14:03:00)
该条数据的水位线时间含义:timestamp小于1543900383(2018-12-04 14:03:00)的数据,都已经到达了。


# PRIMARY KEY
SQL 标准主键限制可以有两种模式:ENFORCED 或者 NOT ENFORCED。 它申明了是否输入/出数据会做合法性检查(是否唯一)。Flink 不存储数据因此只支持 NOT ENFORCED 模式,即不做检查,用户需要自己保证唯一性

# 分区表
-- 创建一个分区表
CREATE TABLE country_page_view (user STRING, cnt INT, date STRING, country STRING)
PARTITIONED BY (date, country)
WITH (...)

-- 追加行到该静态分区中 (date='2019-8-30', country='China')
INSERT INTO country_page_view PARTITION (date='2019-8-30', country='China')
SELECT user, cnt FROM page_view_source;

-- 追加行到分区 (date, country) 中,其中 date 是静态分区 '2019-8-30';country 是动态分区,其值由每一行动态决定
INSERT INTO country_page_view PARTITION (date='2019-8-30')
SELECT user, cnt, country FROM page_view_source;

# DESCRIBE 语句
# USE 语句
# SHOW 语句
SHOW 语句用于列出其相应父对象中的对象,例如 catalog、database、table 和 view、column、function 和 module;SHOW CREATE 语句用于打印给定对象的创建 DDL 语句
SHOW CATALOGS/DATABASES/TABLES/FUNCTIONS/USER FUNCTIONS/MODULES/FULL MODULES/JARS;
SHOW CREATE TABLE my_table;
SHOW COLUMNS from MyUserTable LIKE '%f%';

# EXPLAIN 语句
EXPLAIN 语句用于解释 query 或 INSERT 语句的执行逻辑,也用于优化 query 语句的查询计划。

# LOAD 语句
LOAD 语句用于加载内置的或用户自定义的模块。例如:LOAD MODULE hive WITH ('hive-version' = '3.1.2');
# UNLOAD 语句
# SET 语句 #
SET 语句用于修改配置或展示配置。
用法:SET ('key' = 'value')
如果没有指定 key 和 value,它仅仅打印所有属性。否则,它会为 key 设置指定的 value 值。
例子:SET 'table.local-time-zone' = 'Europe/Berlin'; SET;
# RESET 语句 #
RESET 语句用于将配置重置为默认值。

# JAR 语句 #
JAR 语句用于将用户 jar 添加到 classpath、或将用户 jar 从 classpath 中删除或展示运行时 classpath 中添加的 jar。
ADD JAR '/path/hello.jar';
SHOW JARS;
REMOVE JAR '/path/hello.jar';


# Flink SQL优化
# MiniBatch 聚合
MiniBatch 聚合的核心思想是将一组输入的数据缓存在聚合算子内部的缓冲区中。当输入的数据被触发处理时,每个 key 只需一个操作即可访问状态。这样可以大大减少状态开销并获得更好的吞吐量。但是,这可能会增加一些延迟,因为它会缓冲一些记录而不是立即处理它们。这是吞吐量和延迟之间的权衡
开启这项优化,需要设置选项 table.exec.mini-batch.enabled、table.exec.mini-batch.allow-latency 和 table.exec.mini-batch.size

# Local-Global 聚合
Local-Global 聚合是为解决数据倾斜问题提出的,通过将一组聚合分为两个阶段,首先在上游进行本地聚合,然后在下游进行全局聚合,类似于 MapReduce 中的 Combine + Reduce 模式
local-global 聚合依赖于启用了 mini-batch 优化
// access flink configuration
Configuration configuration = tEnv.getConfig().getConfiguration();
// set low-level key-value options
configuration.setString("table.exec.mini-batch.enabled", "true"); // local-global aggregation depends on mini-batch is enabled
configuration.setString("table.exec.mini-batch.allow-latency", "5 s");
configuration.setString("table.exec.mini-batch.size", "5000");
configuration.setString("table.optimizer.agg-phase-strategy", "TWO_PHASE"); // enable two-phase, i.e. local-global aggregation

# 拆分 distinct 聚合
处理 distinct 聚合

# 在 distinct 聚合上使用 FILTER 修饰符

# 时区

 

# Checkpoint查看

通过观察 checkpoint 记录,我们可以观察任务的运行情况。如果 checkpoint status 失败、或者时间较长,等等,说明程序存在

调整的地方,如 checkpoint 的数据过大,则可使用增量的方式,或者代码需要进行调优处理。如 checkpoint 时间较长,可改用

memory/rockdb 等存储。

链接:
https://www.jianshu.com/p/70fbf632887e
https://www.cnblogs.com/xinjitu-001/p/13799884.html

posted on 2022-06-26 18:52  一笑之奈何  阅读(360)  评论(0编辑  收藏  举报