Flink连续查询中的join

Joins in Continuous Queries

join是批量数据处理中连接两个关系的行的常见且易于理解的操作。 但是,动态表上联接的语义不那么明显,甚至令人困惑。

因此,有两种方法可以使用Table API或SQL实际执行联接。

有关语法的更多信息,请检查Table API和SQL中的连接部分。

  • Regular Joins 常规join
  • Time-windowed Joins 时间窗口join
  • Join with a Temporal Table Function  与时态表函数join
    • Usage
    • Processing-time Temporal Joins
    • Event-time Temporal Joins
  • Join with a Temporal Table 与时态表join
    • Usage

Regular Joins

常规join是最通用的联接类型,其中任何新记录或对联接输入两侧的任何更改都是可见的,并且会影响整个join结果。 例如,如果左侧有一个新记录,则它将与右侧的所有以前和将来的记录合并在一起。

SELECT * FROM Orders
INNER JOIN Product
ON Orders.productId = Product.id

这些语义允许进行任何类型的更新(插入,更新,删除)输入表。

但是,此操作有一个重要的含义:它需要将join输入的两端始终保持在Flink的状态。 因此,如果一个或两个输入表持续增长,资源使用也将无限期增长。

Time-windowed Joins

时间窗口联接由联接谓词定义,该联接谓词检查输入记录的时间属性是否在某些时间限制(即时间窗口)内。

SELECT *
FROM
  Orders o,
  Shipments s
WHERE o.id = s.orderId AND
      o.ordertime BETWEEN s.shiptime - INTERVAL '4' HOUR AND s.shiptime

与常规联接操作相比,这种联接仅支持具有时间属性的仅附加表。 由于时间属性是准单调递增的,因此Flink可以从其状态中删除旧值,而不会影响结果的正确性。

Join with a Temporal Table Function

具有时态表功能的join将附加表(左侧输入/探针侧)与时态表(右侧输入/构建侧)联接,换句话说,即一个随时间变化并跟踪其变化的表。 请检查相应的页面以获取有关时态表的更多信息。

以下示例显示了附加表Orders,该表应该与不断变化的货币汇率表RatesHistory join在一起。

订单是一个附加表,代表给定金额和给定货币的付款。 例如,在10:15时有一笔2欧元的订单。

SELECT * FROM Orders;

rowtime amount currency
======= ====== =========
10:15        2 Euro
10:30        1 US Dollar
10:32       50 Yen
10:52        3 Euro
11:04        5 US Dollar

RatesHistory表示日元汇率(汇率为1)不断变化的附加表。 例如,从09:00到10:45的欧元对日元的汇率为114。从10:45到11:15的汇率为116。

SELECT * FROM RatesHistory;

rowtime currency   rate
======= ======== ======
09:00   US Dollar   102
09:00   Euro        114
09:00   Yen           1
10:45   Euro        116
11:15   Euro        119
11:49   Pounds      108

假设我们要计算所有转换为通用货币(日元)的订单金额。

例如,我们要使用给定行时间(114)的适当转换率转换以下顺序。

rowtime amount currency
======= ====== =========
10:15        2 Euro

如果不使用时态表的概念,则需要编写如下查询:

SELECT
  SUM(o.amount * r.rate) AS amount
FROM Orders AS o,
  RatesHistory AS r
WHERE r.currency = o.currency
AND r.rowtime = (
  SELECT MAX(rowtime)
  FROM RatesHistory AS r2
  WHERE r2.currency = o.currency
  AND r2.rowtime <= o.rowtime);

借助时态表函数Rates over RatesHistory,我们可以在SQL中将查询表示为:

SELECT
  o.amount * r.rate AS amount
FROM
  Orders AS o,
  LATERAL TABLE (Rates(o.rowtime)) AS r
WHERE r.currency = o.currency

probe side(探查表、大表)的记录都会和build side(映射表、小表)与probe side记录时间属性相同的版本进行join。为了支持build side上以前值的更新(覆盖),该表必须定义一个主键。

在我们的示例中,订单中的每条记录与在o.rowtime时间点版本的“费率”join在一起。在之前的示例中,currency字段已被定义为Rates的主键,并且用于连接两个表。如果查询使用的是处理时间概念,则执行操作时,新添加的订单将始终与最新版本的Rates进行关联。

与常规join相反,这意味着如果在build side(映射表、小表)有新记录,则不会影响联接的先前结果。 这再次允许Flink限制必须保留在状态中的元素数量。

与time-window join相比,temporal table join未定义将在界限内进行join的时间窗口。 probe side(探查表、大表)的记录都会与时间属性相同时间点的build side(映射表、小表)版本九路进行join。 因此,构建方面的记录可能是任意旧的。 随着时间的流逝,该记录的先前版本和不再需要的版本(对于给定的主键)将从状态中删除。

这种行为使时态表成为一个很好的候选者,可以用关系术语来表示流的富集。

Usage

定义时态表函数后,我们就可以开始使用它了。 可以使用与普通表函数相同的方式来使用时态表函数。

SELECT
  SUM(o_amount * r_rate) AS amount
FROM
  Orders,
  LATERAL TABLE (Rates(o_proctime))
WHERE
  r_currency = o_currency

注意:对于临时联接,尚未实现在查询配置中定义的状态保留。 这意味着计算查询结果所需的状态可能会无限增长,具体取决于历史记录表的不同主键数量。

Processing-time Temporal Joins

使用处理时间时间属性,不可能将时间属性作为参数传递给时态表函数。 根据定义,它始终是当前时间戳。 因此,处理时间时态表函数的调用将始终返回基础表的最新已知版本,并且基础历史表中的任何更新也将立即覆盖当前值。

仅将构建侧记录的最新版本(相对于定义的主键)保持在该状态。 构建端的更新不会影响先前发出的联接结果。

可以将处理时的临时联接视为一种简单的HashMap ,它存储来自构建端的所有记录。 当来自构建端的新记录与先前的记录具有相同的键时,旧值仅被覆盖。 总是根据HashMap的最新/当前状态评估来自探测器端的每个记录。

Event-time Temporal Joins

利用事件时间时间属性(即,行时间属性),可以将过去的时间属性传递给时间表函数。 这允许在公共时间点将两个表连接在一起。

与处理时临时联接相比,时态表不仅将构建侧记录的最新版本(相对于定义的主键)保持在状态中,而且还存储自上次水印以来的所有版本(按时间标识)。

例如,根据临时表的概念,将附加到探测侧表的事件时间时间戳为12:30:00的输入行与构建侧表的版本在时间12:30:00连接在一起 。 因此,传入行仅与时间戳小于或等于12:30:00的行连接,并根据主键应用更新直到该时间点。

根据事件时间的定义,水印允许连接操作及时向前移动,并丢弃不再需要的构建表版本,因为不需要输入具有更低或相等时间戳的行。

Join with a Temporal Table

与时态表的联接将任意表(左侧输入/探针侧)与时态表(右侧输入/构建侧)联接,即随时间变化的外部尺寸表。 请检查相应的页面以获取有关时态表的更多信息。

用户不能将任意表用作时态表,而需要使用由LookupableTableSource支持的表。 LookupableTableSource只能作为时间表用于时间联接。 有关如何定义LookupableTableSource的更多详细信息,请参见页面。

下面的示例显示了Orders流,该流应与不断变化的货币汇率表LatestRates结合在一起。

LatestRates是使用最新汇率实现的维度表。 在时间10:15、10:30、10:52,LatestRates的内容如下:

10:15> SELECT * FROM LatestRates;

currency   rate
======== ======
US Dollar   102
Euro        114
Yen           1

10:30> SELECT * FROM LatestRates;

currency   rate
======== ======
US Dollar   102
Euro        114
Yen           1


10:52> SELECT * FROM LatestRates;

currency   rate
======== ======
US Dollar   102
Euro        116     <==== changed from 114 to 116
Yen           1

时间10:15和10:30的LastestRates的内容相等。 欧元汇率在10:52从114更改为116。

订单是一个仅附加表,代表给定金额和给定货币的付款。 例如,在10:15时有一笔2欧元的订单。

SELECT * FROM Orders;

amount currency
====== =========
     2 Euro             <== arrived at time 10:15
     1 US Dollar        <== arrived at time 10:30
     2 Euro             <== arrived at time 10:52

假设我们要计算所有转换为通用货币(日元)的订单金额。

例如,我们想使用LatestRates中的最新汇率转换以下订单。 结果将是:

amount currency     rate   amout*rate
====== ========= ======= ============
     2 Euro          114          228    <== arrived at time 10:15
     1 US Dollar     102          102    <== arrived at time 10:30
     2 Euro          116          232    <== arrived at time 10:52

借助时态表联接,我们可以在SQL中将查询表示为:

SELECT
  o.amout, o.currency, r.rate, o.amount * r.rate
FROM
  Orders AS o
  JOIN LatestRates FOR SYSTEM_TIME AS OF o.proctime AS r
  ON r.currency = o.currency

探针端的每个记录都将与构建端表的当前版本关联。 在我们的示例中,查询使用的是处理时间概念,因此在执行操作时,新附加的订单将始终与最新版本的LatestRates结合在一起。 注意,结果对于处理时间不是确定的。

与常规联接相反,尽管在构建方面进行了更改,但临时表联接的先前结果将不会受到影响。 而且,时态表联接运算符非常轻巧,并且不保留任何状态。

与时间窗口联接相比,时态表联接没有定义将要在其中联接记录的时间窗口。 在处理时,探测端的记录始终会与构建端的最新版本结合在一起。 因此,构建方面的记录可能是任意旧的。

时态表函数联接和时态表联接都来自相同的动机,但是具有不同的SQL语法和运行时实现:

  • 时态表函数联接的SQL语法是联接UDTF,而时态表联接使用SQL:2011中引入的标准时态表语法。
  • 临时表函数联接的实现实际上联接了两个流并使它们保持状态,而临时表联接仅接收唯一的输入流并根据记录中的键查找外部数据库。
  • 临时表函数联接通常用于联接变更日志流,而临时表联接通常用于联接外部表(即维表)。

这种行为使时态表成为一个很好的候选者,可以用关系术语来表示流的富集。

将来,时态表联接将支持时态表功能联接的功能,即支持时态联接变更日志流。

Usage

临时表联接的语法如下:

SELECT [column_list]
FROM table1 [AS ]
[LEFT] JOIN table2 FOR SYSTEM_TIME AS OF table1.proctime [AS ]
ON table1.column-name1 = table2.column-name1

当前,仅支持INNER JOIN和LEFT JOIN。 临时表之后应遵循FOR SYSTEM_TIME AS OF table1.proctime。 proctime是table1的处理时间属性。 这意味着在连接左表中的每个记录时,它会在处理时为时态表拍摄快照。

例如,在定义时态表之后,我们可以如下使用它。

SELECT
  SUM(o_amount * r_rate) AS amount
FROM
  Orders
  JOIN LatestRates FOR SYSTEM_TIME AS OF o_proctime
  ON r_currency = o_currency

Attention:仅在Blink计划程序中受支持。

Attention:它仅在SQL中受支持,而在Table API中尚不支持。

Attention:Flink当前不支持事件时间时态表联接。

---翻译自flink官方文档

 

你可能感兴趣的:(flink)