我有两个(流)表,一个带有事件时间列,另一个没有。我想使用Table API加入这些信息,但还没有找到一种方法来保留时间戳信息。
请考虑以下可在Scala REPL中执行的MWE:
import org.apache.flink.streaming.api.TimeCharacteristic
import org.apache.flink.streaming.api.functions.ProcessFunction
import org.apache.flink.streaming.api.functions.source.SourceFunction
import org.apache.flink.streaming.api.scala._
import org.apache.flink.table.api.{Table, TableEnvironment}
import org.apache.flink.table.api.scala._
import org.apache.flink.types.Row
import org.apache.flink.util.Collector
val streamEnv: StreamExecutionEnvironment = senv
streamEnv.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)
val tableEnv = TableEnvironment.getTableEnvironment(streamEnv)
val table1 = streamEnv.addSource(new SourceFunction[(Long, String)] {
override def run(ctx: SourceFunction.SourceContext[(Long, String)]): Unit = {
ctx.collectWithTimestamp((1L, "hello"), 1L)
}
override def cancel(): Unit = ???
}).toTable(tableEnv, 'ts.rowtime, 'column1)
val table2 = streamEnv.addSource(new SourceFunction[(String, String)] {
override def run(ctx: SourceFunction.SourceContext[(String, String)]): Unit = {
ctx.collect(("hello", "world"))
}
override def cancel(): Unit = ???
}).toTable(tableEnv, 'column2, 'column3)
def checkTable(table: Table): Unit = {
table
.toAppendStream[Row]
.process(new ProcessFunction[Row, Int] {
override def processElement(value: Row, ctx: ProcessFunction[Row, Int]#Context, out: Collector[Int]): Unit = {
out.collect((ctx.timestamp() / 1000).toInt)
}
})
streamEnv.execute()
}
checkTable(table1)
checkTable(table1.join(table2, 'column1 === 'column2).select('column1, 'column2, 'column3))
第一个表显然已分配了事件时间,因此对checkTable
的第一次调用成功。 (虽然很奇怪,这仅在从数据流创建表时显式提供.rowtime
标签时才有效。)
在第一张和第二张表的联接上调用checkTable
会导致
Caused by: java.lang.NullPointerException
at scala.Predef$.Long2long(Predef.scala:363)
at $anon$1.processElement(<console>:81)
at $anon$1.processElement(<console>:79)
at org.apache.flink.streaming.api.operators.ProcessOperator.processElement(ProcessOperator.java:66)
ie ctx.timestamp()
在ProcessFunction.processElement
中为空。我可以通过在连接结果上调用.assignAscendingTimestamps(...)
来强制执行时间戳记,但是我不认为这是安全的,因为我不知道连接如何影响排序。是否可以使此连接工作并保留时间戳?
答案 0 :(得分:2)
通用联接运算符无法保留事件时间戳属性,因为记录可以按任何顺序联接。
正在左侧输入连接运算符的记录可能会与两天前从右侧提取的记录进行连接。反之亦然,也就是说,左侧的记录要等待一段时间才能从右侧的匹配记录到达。没有范围允许发出有意义的水印。因此,所有输入记录的事件时间属性都将丢失,它们只能被视为常规的TIMESTAMP
属性。
但是,您可以使用windowed join,即基本上是一个附加的连接条件来限制记录之间的延迟:
.where("a = d && ltime >= rtime - 5.minutes && ltime < rtime + 10.minutes")
可以自由选择间隔。在这种情况下,Flink可以推断时间戳和水印的界限,并能够保留事件时间属性。