flink流計算隨筆(5)

Windows
聚合事件(例如計數、和)在流上的工作方式與批處理不同。例如,不可能計算流中的所有元素,因爲流通常是無限的(×××的)。相反,流上的聚合(計數、和等)是由窗口 windows限定作用域的,例如“過去5分鐘的計數”或“最後100個元素的總和”。

Windows可以是時間驅動(示例:每30秒)或數據驅動(示例:每100個元素)。一個典型的方法是區分不同類型的窗口,比如翻筋斗窗口(沒有重疊)、滑動窗口(有重疊)和會話窗口(中間有一個不活躍的間隙)。

Time
當提到流程序中的時間(例如定義窗口)時,可以指不同的時間概念:

事件時間Event Time 是創建事件的時間。它通常由事件中的時間戳描述,例如由生產傳感器或生產服務附加的時間戳。Flink通過timestamp assigners(時間戳指定人)訪問事件時間戳。

攝入時間Ingestion time 是事件進入源操作符的Flink數據流的時間。

處理時間Processing Time是執行基於時間的操作的每個操作符的本地時間。

有狀態操作(Stateful Operations)
雖然數據流中的許多操作一次只查看一個單獨的事件(例如事件解析器),但是一些操作記住了跨多個事件的信息(例如窗口操作符)。這些操作稱爲有狀態操作。

有狀態操作的狀態被維護在可以認爲是嵌入式鍵/值存儲中。狀態與有狀態操作符讀取的流一起被嚴格地分區和分佈。因此,在keyBy()函數之後,只能在鍵控流上訪問鍵/值狀態,並且只能訪問與當前事件的鍵相關聯的值。對齊流和狀態的鍵確保所有的狀態更新都是本地操作,保證一致性而不增加事務開銷。這種對齊還允許Flink透明地重新分配狀態和調整流分區。
容錯檢查點Checkpoints for Fault Tolerance
Flink通過流回放和檢查點的組合實現了容錯。檢查點與每個輸入流中的特定點以及每個操作符的對應狀態相關。通過恢復操作符的狀態並從檢查點重新播放事件,流數據流可以在檢查點恢復,同時保持一致性(準確地說是一次處理語義)。

檢查點間隔是在執行期間用恢復時間(需要重放的事件數量)來權衡容錯開銷的一種方法。

關於容錯的內部描述提供了關於Flink如何管理檢查點和相關主題的更多信息。有關啓用和配置檢查點的詳細信息在檢查點API文檔中。

批處理流Batch on Streamin
Flink執行批處理程序作爲流程序的特殊情況,其中流是有界的(有限的元素數量)。數據集在內部被視爲數據流。因此,上述概念同樣適用於批處理程序,也適用於流程序,但有少數例外:

批處理程序的容錯不使用檢查點。恢復通過完全重放流來實現。這是可能的,因爲輸入是有界的。這將使成本更多地用於恢復,但使常規處理更便宜,因爲它避免了檢查點。

數據集API中的有狀態操作使用簡化的內存/核心外數據結構,而不是鍵/值索引。

DataSet API引入了特殊的synchronized(基於超步的)迭代,這只能在有界的流上實現。
Flink中的DataStream程序是在數據流上實現轉換的常規程序(例如,過濾、更新狀態、定義窗口、聚合)。數據流最初是從各種來源(例如,消息隊列、套接字流、文件)創建的。結果通過sink返回,它可以將數據寫入文件或寫入標準輸出(例如命令行終端)。Flink程序在各種上下文中運行,獨立運行,或嵌入到其他程序中。執行可以在本地JVM中執行,也可以在許多機器的集羣中執行。
下面的程序是一個完整的流窗口單詞計數應用程序的工作示例,它在5秒的窗口中對來自web套接字的單詞進行計數。您可以複製並粘貼代碼在本地運行它。

import org.apache.flink.streaming.api.scala._
import org.apache.flink.streaming.api.windowing.time.Time

object WindowWordCount {
  def main(args: Array[String]) {

    val env = StreamExecutionEnvironment.getExecutionEnvironment
    val text = env.socketTextStream("localhost", 9999)

    val counts = text.flatMap { _.toLowerCase.split("\\W+") filter { _.nonEmpty } }
      .map { (_, 1) }
      .keyBy(0)
      .timeWindow(Time.seconds(5))
      .sum(1)

    counts.print()

    env.execute("Window Stream WordCount")
  }
}

要運行示例程序,首先從終端啓動netcat的輸入流:
只需鍵入一些單詞,然後按下回車鍵就可以得到一個新詞。這些將是單詞計數程序的輸入。如果你想看到數大於1,輸入相同的單詞一遍又一遍地在5秒(如果你不能快速敲鍵盤,增加窗口大小的5秒內☺)

Table API & SQL
Apache Flink具有兩個用於統一流和批處理的關係API——Table API和SQL。Table API是Scala和Java的語言集成查詢API,允許從關係操作符(如選擇、篩選和以非常直觀的方式連接)中組合查詢。Flink的SQL支持基於實現SQL標準的Apache Calcite。無論輸入是批輸入(數據集)還是流輸入(DataStream),任何接口中指定的查詢都具有相同的語義並指定相同的結果。

Table API和SQL接口以及Flink的DataStream和DataSet API緊密集成在一起。您可以很容易地在所有api和基於這些api的庫之間切換。例如,您可以使用CEP庫從數據流中提取模式,然後使用表API分析模式,或者在對預處理數據運行Gelly圖形算法之前,您可以使用SQL查詢掃描、過濾和聚合批處理表。

請注意,Table API和SQL的特性還不完整,正在積極開發中。不是所有的操作都被[Table API, SQL]和[stream, batch]輸入的每個組合所支持。

SQL標準的Apache Calcite

statement:
      setStatement
  |   resetStatement
  |   explain
  |   describe
  |   insert
  |   update
  |   merge
  |   delete
  |   query

setStatement:
      [ ALTER ( SYSTEM | SESSION ) ] SET identifier '=' expression

resetStatement:
      [ ALTER ( SYSTEM | SESSION ) ] RESET identifier
  |   [ ALTER ( SYSTEM | SESSION ) ] RESET ALL

explain:
      EXPLAIN PLAN
      [ WITH TYPE | WITH IMPLEMENTATION | WITHOUT IMPLEMENTATION ]
      [ EXCLUDING ATTRIBUTES | INCLUDING [ ALL ] ATTRIBUTES ]
      [ AS JSON | AS XML ]
      FOR ( query | insert | update | merge | delete )

describe:
      DESCRIBE DATABASE databaseName
   |  DESCRIBE CATALOG [ databaseName . ] catalogName
   |  DESCRIBE SCHEMA [ [ databaseName . ] catalogName ] . schemaName
   |  DESCRIBE [ TABLE ] [ [ [ databaseName . ] catalogName . ] schemaName . ] tableName [ columnName ]
   |  DESCRIBE [ STATEMENT ] ( query | insert | update | merge | delete )

insert:
      ( INSERT | UPSERT ) INTO tablePrimary
      [ '(' column [, column ]* ')' ]
      query

update:
      UPDATE tablePrimary
      SET assign [, assign ]*
      [ WHERE booleanExpression ]

assign:
      identifier '=' expression

merge:
      MERGE INTO tablePrimary [ [ AS ] alias ]
      USING tablePrimary
      ON booleanExpression
      [ WHEN MATCHED THEN UPDATE SET assign [, assign ]* ]
      [ WHEN NOT MATCHED THEN INSERT VALUES '(' value [ , value ]* ')' ]

delete:
      DELETE FROM tablePrimary [ [ AS ] alias ]
      [ WHERE booleanExpression ]

query:
      values
  |   WITH withItem [ , withItem ]* query
  |   {
          select
      |   selectWithoutFrom
      |   query UNION [ ALL | DISTINCT ] query
      |   query EXCEPT [ ALL | DISTINCT ] query
      |   query MINUS [ ALL | DISTINCT ] query
      |   query INTERSECT [ ALL | DISTINCT ] query
      }
      [ ORDER BY orderItem [, orderItem ]* ]
      [ LIMIT [ start, ] { count | ALL } ]
      [ OFFSET start { ROW | ROWS } ]
      [ FETCH { FIRST | NEXT } [ count ] { ROW | ROWS } ONLY ]

withItem:
      name
      [ '(' column [, column ]* ')' ]
      AS '(' query ')'

orderItem:
      expression [ ASC | DESC ] [ NULLS FIRST | NULLS LAST ]

select:
      SELECT [ STREAM ] [ ALL | DISTINCT ]
          { * | projectItem [, projectItem ]* }
      FROM tableExpression
      [ WHERE booleanExpression ]
      [ GROUP BY { groupItem [, groupItem ]* } ]
      [ HAVING booleanExpression ]
      [ WINDOW windowName AS windowSpec [, windowName AS windowSpec ]* ]

selectWithoutFrom:
      SELECT [ ALL | DISTINCT ]
          { * | projectItem [, projectItem ]* }

projectItem:
      expression [ [ AS ] columnAlias ]
  |   tableAlias . *

tableExpression:
      tableReference [, tableReference ]*
  |   tableExpression [ NATURAL ] [ ( LEFT | RIGHT | FULL ) [ OUTER ] ] JOIN tableExpression [ joinCondition ]
  |   tableExpression CROSS JOIN tableExpression
  |   tableExpression [ CROSS | OUTER ] APPLY tableExpression

joinCondition:
      ON booleanExpression
  |   USING '(' column [, column ]* ')'

tableReference:
      tablePrimary
      [ matchRecognize ]
      [ [ AS ] alias [ '(' columnAlias [, columnAlias ]* ')' ] ]

tablePrimary:
      [ [ catalogName . ] schemaName . ] tableName
      '(' TABLE [ [ catalogName . ] schemaName . ] tableName ')'
  |   tablePrimary [ EXTEND ] '(' columnDecl [, columnDecl ]* ')'
  |   [ LATERAL ] '(' query ')'
  |   UNNEST '(' expression ')' [ WITH ORDINALITY ]
  |   [ LATERAL ] TABLE '(' [ SPECIFIC ] functionName '(' expression [, expression ]* ')' ')'

columnDecl:
      column type [ NOT NULL ]

values:
      VALUES expression [, expression ]*

groupItem:
      expression
  |   '(' ')'
  |   '(' expression [, expression ]* ')'
  |   CUBE '(' expression [, expression ]* ')'
  |   ROLLUP '(' expression [, expression ]* ')'
  |   GROUPING SETS '(' groupItem [, groupItem ]* ')'

windowRef:
      windowName
  |   windowSpec

windowSpec:
      [ windowName ]
      '('
      [ ORDER BY orderItem [, orderItem ]* ]
      [ PARTITION BY expression [, expression ]* ]
      [
          RANGE numericOrIntervalExpression { PRECEDING | FOLLOWING }
      |   ROWS numericExpression { PRECEDING | FOLLOWING }
      ]
      ')'

在insert中,如果insert或UPSERT語句沒有指定目標列的列表,查詢的列數必須與目標表相同,除非是在某些一致性級別。

在merge中,至少有一個匹配時和未匹配時的子句必須出現。

tablePrimary可能只包含特定符合性級別的擴展子句;在這些相同的一致性級別中,insert中的任何列都可以被columnDecl替換,其效果類似於將其包含在EXTEND子句中。

在orderItem中,如果表達式是正整數n,它表示SELECT子句中的第n項。

在查詢中,count和start可以是無符號整型字面值,也可以是值爲整型的動態參數。
aggregate聚合查詢是包含GROUP BY或HAVING子句或SELECT子句中的聚合函數的查詢。在SELECT中,具有和ORDER BY子句的聚合查詢中,所有表達式都必須是當前組中的常量(即,按照group BY子句或常量的定義對常量進行分組)、聚合函數或常量與聚合函數的組合。聚合和分組函數只能出現在聚合查詢中,而且只能出現在SELECT、HAVING或ORDER BY子句中。
標量子查詢是用作表達式的子查詢。如果子查詢不返回行,則該值爲空;如果它返回多個行,則爲錯誤。

IN、EXISTS和scalar子查詢可以出現在表達式的任何地方(例如SELECT子句、where子句、ON子句連接或聚合函數的參數)。

一個IN、EXISTS或scalar子查詢可能相互關聯;也就是說,它可以引用包含查詢的FROM子句中的表。

selectWithoutFrom等價於值,但不是標準SQL,只允許在某些符合級別中使用。

MINUS相當於EXCEPT,但不是標準SQL,只允許在某些一致性級別上使用。

交叉應用和外部應用只允許在某些符合級別。

“限制開始,計數”相當於“限制計數偏移開始”,但只允許在某些符合級別。“LIMIT start, count” is equivalent to “LIMIT count OFFSET start” but is only allowed in certain conformance levels.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章