2019-03-09-Flink(6)——flink table & sql 介紹

本文轉自個人微信公衆號,原文鏈接。本博客評論系統需要梯子,大家關注下公衆號方便交流。

本文基於 Flink 1.7。

隨着 Hadoop 的發展,有了Hive,使用HQL 即可完成原來繁瑣的Map Reduce 程序。

隨着 Spark的發展,引入了 Spark SQL。

隨着 Flink 版本的更迭,Flink 也提供了Flink SQL,以及 Table APIs。

注意:截止 Flink 1.7,Table API 和 SQL 還沒有完成,有些操作還不支持。

1. 基本概念

1.1 Why

那麼,爲什麼要推出Table APIs和SQL?

首先,來看下Flink 的編程模型。如下圖所示(圖片來源於官網),DataStream API 和 DataSet API 是分開的,但是對於應用開發者來說,爲什麼要關注這一點?對於相同的數據,批處理與流計算居然要寫兩套代碼,簡直不可思議。Table APIs和SQL的推出,實現了流處理和批處理的統一。

其次,降低了學習和使用門檻,基於 DataStream/DataSet APIs 的 Scala 或 Java 程序開發,對於BI/分析師來說,還是有一定門檻的,而SQL 則簡單太多了。

1.2 Dynamic Tables

Dynamic Tables 是 Flink Table API 和 SQL的核心概念,與大家熟知的Static Tables 相比,Dynamic Tables 隨着時間一直在變化。可以查詢Dynamic Table,查詢Dynamic Table 時會產生一個持續的查詢,持續的查詢不會終止,產生的結果也是Dynamic Table,根據輸入,輸出也會不斷變化。熟悉關係型數據庫的可以將Dynamic Tables的查詢跟關係型數據庫裏查詢物化視圖對比起來,需要注意的是,Dynamic Tables 是一個邏輯概念,不需要(全部)物化。

另外,需要注意,在Dynamic Table上的持續查詢的結果語義上是跟在Dynamic Table的快照上執行查詢相同。

如上圖所示:

  • Stream 轉化爲 Dynamic Table
  • 在Dynamic Table 上執行查詢,得到的結果是一個新的Dynamic Table
  • 最終的Dynamic Table 結果,被轉化爲 Stream

1.3 Update Queries VS Append Queries

Append Queries:只會對查詢結果進行追加的查詢。

Update Queries:會更新查詢結果的查詢,一般需要維護更多的state。

1.4 查詢限制

有些 Stream 上的查詢需要花費巨大的代價:

  • 需要維護的state 太大。持續查詢可能會運行非常長的時間,處理的數據量會非常大,對於一些需要更新原來結果的查詢,需要維護原來的結果,維護的state會很大。
  • 更新計算代價高昂:輸入數據的一小點變化,可能有些查詢需要重新計算大量的數據,這種計算就不適合做持續查詢。

1.5 Table 到 Stream 的轉化

就像普通的數據庫Table 一樣,Dynamic Table也支持 insertupdatedelete等對它的更新。當需要將Dynamic Table 轉化爲 Stream 或者輸出到外部系統時,需要對這些更新進行encode

  • Append-only Stream:僅有Insert 更新的Dynamic Table,可通過emit 插入的數據行轉化爲stream。
  • Retract Stream:Retract Stream 是支持 add 消息 和 retract 消息兩類消息的流。將insert 編碼爲add 消息、將delete 編碼爲retract 消息、將update 編碼爲對之前消息的retarct 消息和對新消息的add消息。
  • Upsert Stream:Upsert Stream 是支持upsert 消息和delete消息兩類消息的流。如果一個Dynamic Table需要轉化爲一個Upsert Stream,這個Table 必須要有unique key,可以將insert/update編碼爲upsert 消息,將delete 編碼爲delete消息。Upsert Stream 與 Retract Stream的主要區別是update 操作只需要一條消息,所以會更高效。

Append-only Stream 和 Retarct Stream 支持將Dynamic Table 轉化爲DataStream。

2. 實戰

下面引入一個簡單的例子,從stream開始,轉化爲 Table,然後查詢Table,最後將Table 轉化爲Stream。

從例子可以很容易的看出,Stream 和 Table APIs / SQL 可以很容易的混用,這也給我們帶來了極大的便利性。

2.1 引入依賴

        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-table_2.11</artifactId>
            <version>${flink.version}</version>
        </dependency>
        <dependency>
            <!-- for batch query -->
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-scala_${scala.binary.version}</artifactId>
            <version>${flink.version}</version>
            <!-- 上線時用provided,避免build的jar包太大,更避免衝突 -->
            <!--<scope>provided</scope>-->
            <!-- IDEA 裏用compile,否則in-ide execution會失敗 -->
            <scope>compile</scope>
        </dependency>
        <dependency>
            <!-- for streaming query -->
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-streaming-scala_${scala.binary.version}</artifactId>
            <version>${flink.version}</version>
            <!-- 上線時用provided,避免build的jar包太大,更避免衝突 -->
            <!--<scope>provided</scope>-->
            <!-- IDEA 裏用compile,否則in-ide execution會失敗 -->
            <scope>compile</scope>
        </dependency>

2.2 隱式轉換

Flink 的 Scala Table APIs用了隱式轉換,所以,需要import 進來。

    import org.apache.flink.table.api.scala._
    import org.apache.flink.api.scala._

2.3 創建 TableEnvironment

TableEnvironment 是 Table APIs 和 SQL 的核心,可以用於:

  • 註冊Table
  • 執行SQL 查詢
  • 註冊UDF
  • 將DataStream / DataSet 轉化爲 Table
  • 維護一個到ExecutionEnvironment或StreamExecutionEnvironment的引用。

Table 總是綁定到一個 TableEnvironment的,在使用時,在同一個SQL中不能聯合使用不同TableEnvironment的表,比如joinunion

下面創建一個用於Stream的StreamTableEnvironment。另外,創建一個簡單的stream。

    // 創建StreamTableEnvironment
    val senv = StreamExecutionEnvironment.getExecutionEnvironment
    val stableEnv: StreamTableEnvironment = TableEnvironment.getTableEnvironment(senv)

    // 創建一個用於實驗的 Stream[ObjPrice]
    case class ObjPrice (name: String, price: Long)
    val stream: DataStream[ObjPrice] = senv.fromCollection(List(ObjPrice("car", 100000), ObjPrice("house", 2000000), ObjPrice("book", 100), ObjPrice("car", 900210)))

2.4 將Stream 轉化爲 Table

    val sTable1Rename: Table = stableEnv.fromDataStream(stream, 'myName, 'myPrice)

將上面的stream 轉化爲 Table,同時對字段進行重命名。

2.5 查詢 Table

    // 採用Table API 的方式進行查詢
    val sTableResult: Table = sTable1Rename
      .filter('myPrice > 1000)
      .groupBy('myName)
      .select('myName, 'myPrice.sum as 'mySumPrice)

2.6 將 Table 轉化爲 Stream

    val sResultDataStream: DataStream[(Boolean, ObjPrice)] = stableEnv.toRetractStream[ObjPrice](sTableResult)

3. 總結

本文僅涉及一些基礎知識和最常見的使用,其他的比如註冊 Table / TableSink / TableSource / External Catalog 、數據類型與Table Schema的映射、查詢優化等並不涉及,可以參考官網 進行查閱。

爲了方便交流,請掃描下方二維碼關注我。


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