騰訊面試:Flink 五種 Join 方式,各自有什么特點,應用場景分別是怎么樣的?
一、Flink Join概述
在大數據處理領域,Flink是一個強大的開源流處理框架,廣泛應用于實時數據分析、事件處理等場景。Join操作作為數據處理中的核心操作之一,在Flink中也有著豐富的實現方式。Flink的Join功能允許用戶將多個數據流或數據集按照一定的條件進行關聯,從而得到更有價值的信息。
1. Join的應用場景
Join操作在實際業務中有廣泛的應用場景,以下是一些常見的例子:
- 曝光關聯點擊:幾乎所有公司的APP都會涉及到曝光關聯點擊的分析。例如,分析用戶在看到某個廣告后是否進行了點擊操作,通過將曝光日志和點擊日志進行Join操作,可以得到更全面的用戶行為數據。
- 維度拼接:在數據處理中,經常需要將不同維度的數據進行拼接,以構建寬表。比如,將用戶信息表和訂單信息表進行Join,得到包含用戶詳細信息的訂單寬表。
- 電商退單分析:在電商場景中,分析退單的訂單關聯下單的訂單,可以了解退單訂單的特點,如退單原因、退單時間等。
2. 批Join和流Join的區別
在批式計算和流式計算中,Join操作有著明顯的區別:
- 批式計算:在批式計算中,Join的左右表都是“全集”,可以在全集上進行關聯操作。常見的技術方案有sort - merge、hash join等,這些方案已經非常成熟。例如,在離線數倉中,使用Hive SQL進行Join操作時,可以很方便地對左右表進行關聯。
- 流式計算:在流式計算中,左右表的數據都是無界的,并且是實時到來的。這會帶來一些問題,例如:
- 數據到達時間不確定:流式數據到達計算引擎的時間不一定,比如A流的數據先到了,A流不知道B流對應同key的數據什么時候到,無法進行關聯,這會影響數據質量。
- 數據下發問題:A流的數據到達后,如果B流的數據永遠不到,那么A流的數據在什么時候以及是否要填充一個null值下發下去,這涉及到數據時效問題。
二、Flink各種Join類型
Flink提供了多種Join類型,以滿足不同的業務需求。下面將詳細介紹各種Join類型的定義、原理、適用場景和Flink SQL樣例代碼。
1. Regular Join(常規連接)
(1) 定義
Regular Join是最通用的Join類型,在這種Join下,Join兩側表的任何新記錄或變更都是可見的,并會影響整個Join的結果。對于流式查詢,Regular Join的語法非常靈活,允許輸入表進行任何類型的更新(插入、更新、刪除)。
(2) 原理
Regular Join基于無界數據進行關聯,Flink需要將Join輸入的兩邊數據永遠保持在狀態中。因此,計算查詢結果所需的狀態可能會無限增長,這取決于所有輸入表的輸入數據量。為了防止狀態過大,可以提供一個合適的狀態time - to - live (TTL) 配置,但這樣做可能會影響查詢的正確性。
(3) 適用場景
Regular Join適用于離線場景和小數據量場景。例如,在實時數倉建設中,對少量數據進行實時關聯計算。
(4) Flink SQL樣例代碼
Regular Join包含以下幾種常見類型:
- Inner Equi - JOIN:根據join限制條件返回一個簡單的笛卡爾積,目前只支持equi - joins,即至少有一個等值條件。
SELECT*
FROM Orders
INNERJOIN Product
ON Orders.product_id = Product.id;
- Left Join:左流數據到達之后,無論有沒有Join到右流的數據,都會輸出。如果Join到輸出 [L, R] ,沒Join到輸出 [L, null] 。如果右流之后數據到達之后,發現左流之前輸出過沒有Join到的數據,則會發起回撤流,先輸出 - [L, null] ,然后輸出 + [L, R] 。
SELECT*
FROM Orders
LEFTJOIN Product
ON Orders.product_id = Product.id;
- Right Join:右流數據到達之后,無論有沒有Join到左流的數據,都會輸出。如果Join到輸出 [L, R] ,沒Join到輸出 [null, R] 。如果左流之后數據到達之后,發現右流之前輸出過沒有Join到的數據,則會發起回撤流,先輸出 - [null, R] ,然后輸出 + [L, R] 。
SELECT*
FROM Orders
RIGHTJOIN Product
ON Orders.product_id = Product.id;
- Full Outer Join:左流或者右流的數據到達之后,無論有沒有Join到另外一條流的數據,都會輸出。如果一條流的數據到達之后,發現之前另一條流之前輸出過沒有Join到的數據,則會發起回撤流。
SELECT*
FROM Orders
FULLOUTERJOIN Product
ON Orders.product_id = Product.id;
2. Window Join(窗口連接)
(1) 定義
Window Join是將兩條流的數據從無界數據變為有界數據,即劃分出時間窗口,然后將同一時間窗口內的兩條流的數據做Join。這里的時間窗口支持Tumbling(滾動窗口)、Sliding(滑動窗口)、Session(會話窗口)。
(2) 原理
Window Join的底層原理是將兩條實時流數據緩存在Window State中,當窗口觸發計算時,執行Join操作。在窗口內,將具有相同key的元素進行關聯。
(3) 適用場景
Window Join適用于需要在一定時間范圍內進行數據關聯的場景,例如,統計某個時間段內的用戶行為數據。
(4) Flink SQL樣例代碼
以下是一個Inner Window Join的示例:
SELECT...
FROM l [INNER]JOIN r -- l和r是應用了窗口函數的關系
ON l.window_start = r.window_start AND l.window_end = r.window_end AND...
3. Interval Join(區間連接)
(1) 定義
Interval Join允許一條流去Join另一條流中前后一段時間內的數據。它通過定義一個時間區間,將兩條流在該區間內的數據進行關聯。
(2) 原理
Interval Join的底層實現是同時保留2個流一定時間,這樣一條流既可以關聯另外一個流過去一段時間范圍內的數據,還能關聯另外一個流未來一段時間內的數據。在實現時,需要指定時間類型為eventtime,并且通過between方法控制時間范圍。
(3) 適用場景
Interval Join適用于需要在一定時間區間內進行數據關聯的場景,例如,統計在下單一小時內付款的訂單信息。
(4) Flink SQL樣例代碼
SELECT...
FROM t1 JOIN t2
ON t1.key= t2.keyAND t1.timestampBETWEEN t2.timestamp+INTERVAL'lower_bound'AND t2.timestamp+INTERVAL'upper_bound'
4. Temporal Join(時態連接)
(1) 定義
Temporal Join允許與版本化表進行連接,可以在某個時間點上獲取版本化表的特定時間版本數據。在Flink SQL中,使用 for system_time as of 語法來執行此操作。
(2) 原理
Temporal Join會將一個表(左輸入/探測側)的每一行與版本化表(右輸入/構建側)中相應行的相關版本進行關聯。Flink會根據明細表中的時間版本選擇Versioned Table對應時間區間內的快照數據進行join。
(3) 適用場景
Temporal Join適用于需要獲取歷史版本數據的場景,例如,實時的根據匯率計算總金額,不同時間點的匯率不同,需要根據訂單時間獲取相應的匯率進行計算。
(4) Flink SQL樣例代碼
SELECT
order_id,
price,
currency,
conversion_rate,
order_time
FROM orders
LEFTJOIN currency_rates FOR SYSTEM_TIME ASOF orders.order_time
ON orders.currency = currency_rates.currency
5. Lookup Join(查找連接)
(1) 定義
Lookup Join是一種特殊的Temporal Join,它使用處理時間作為時間屬性。在Flink中,處理時間是機器的系統時間,也稱為“墻鐘時間”。當使用處理時間進行Join時,Flink會將其轉換為Lookup Join,并使用版本化表的最新版本。
(2) 原理
Lookup Join會在查詢時實時查找外部數據源中的最新數據,以豐富數據流。它要求一個表必須包含處理時間屬性,另一個表必須是維度表。
(3) 適用場景
Lookup Join適用于需要實時獲取外部維度數據的場景,例如,將Kafka的流數據與MySQL的維度數據進行關聯。
(4) Flink SQL樣例代碼
SELECT o.order_id, o.total, c.country, c.zip
FROM orders as o
JOIN customers FOR SYSTEM_TIME ASOF PROCTIME()
ON o.customer_id = c.id
Flink提供了多種Join類型,每種Join類型都有其特點和適用場景。在實際應用中,需要根據具體的業務需求選擇合適的Join類型:
- Regular Join:語法靈活,支持各種更新操作,但需要注意狀態無限增長的問題,適用于離線和小數據量場景。
- Window Join:通過劃分時間窗口將無界數據變為有界數據,適用于在一定時間范圍內進行數據關聯的場景。
- Interval Join:允許在一定時間區間內進行數據關聯,避免了回撤流的產生,適用于對時間區間有要求的場景。
- Temporal Join:可以獲取版本化表的特定時間版本數據,適用于需要歷史版本數據的場景。
- Lookup Join:使用處理時間,實時查找外部數據源的最新數據,適用于實時獲取外部維度數據的場景。
通過合理使用Flink的各種Join類型,可以更好地滿足不同業務場景下的數據處理需求,提高數據處理的效率和準確性。