寫在前面: 部落客是一名大資料的初學者,昵稱來源于《愛麗絲夢遊仙境》中的Alice和自己的昵稱。作為一名網際網路小白,<code>寫部落格一方面是為了記錄自己的學習曆程,一方面是希望能夠幫助到很多和自己一樣處于起步階段的萌新</code>。由于水準有限,部落格中難免會有一些錯誤,有纰漏之處懇請各位大佬不吝賜教!部落格首頁:https://blog.51cto.com/u_15105906 盡管目前水準可能不及各位大佬,但我還是希望自己能夠做得更好,因為<code>一天的生活就是一生的縮影</code>。我希望<code>在最美的年華,做最好的自己</code>!
近幾年Flink發展得異常的火熱,對Flink還不太了解的朋友可以先去看看部落客的上一篇文章????《簡單告訴你,為什麼要學 Flink,Flink 優勢在哪?》。本篇文章,既Hadoop,Hive,Spark之後,推出了關于Flink的大資料面試殺招,希望各位朋友們看完能受益~
Flink基礎
Flink 是一個架構和分布式處理引擎,用于對無界和有界資料流進行有狀态計算。并且 Flink 提供了資料分布、容錯機制以及資源管理等核心功能。Flink提供了諸多高抽象層的API以便使用者編寫分布式任務:
DataSet API, 對靜态資料進行批處理操作,将靜态資料抽象成分布式的資料集,使用者可以友善地使用Flink提供的各種操作符對分布式資料集進行處理,支援Java、Scala和Python。
DataStream API,對資料流進行流處理操作,将流式的資料抽象成分布式的資料流,使用者可以友善地對分布式資料流進行各種操作,支援Java和Scala。
Table API,對結構化資料進行查詢操作,将結構化資料抽象成關系表,并通過類SQL的DSL對關系表進行各種查詢操作,支援Java和Scala。
此外,Flink 還針對特定的應用領域提供了領域庫,例如: Flink ML,Flink 的機器學習庫,提供了機器學習Pipelines API并實作了多種機器學習算法, Gelly,Flink 的圖計算庫,提供了圖計算的相關API及多種圖計算算法實作。
這個問題是一個非常宏觀的問題,因為兩個架構的不同點非常之多。但是在面試時有非常重要的一點一定要回答出來:Flink 是标準的實時處理引擎,基于事件驅動。而 Spark Streaming 是微批(Micro-Batch)的模型 。
下面我們就分幾個方面介紹兩個架構的主要差別:
架構模型Spark Streaming 在運作時的主要角色包括:Master、Worker、Driver、Executor,Flink 在運作時主要包含:Jobmanager、Taskmanager和Slot。
任務排程Spark Streaming 連續不斷的生成微小的資料批次,建構有向無環圖DAG,Spark Streaming 會依次建立 DStreamGraph、JobGenerator、JobScheduler。Flink 根據使用者送出的代碼生成 StreamGraph,經過優化生成 JobGraph,然後送出給 JobManager進行處理,JobManager 會根據 JobGraph 生成 ExecutionGraph,ExecutionGraph 是 Flink 排程最核心的資料結構,JobManager 根據 ExecutionGraph 對 Job 進行排程。
時間機制Spark Streaming 支援的時間機制有限,隻支援處理時間。 Flink 支援了流處理程式在時間上的三個定義:處理時間、事件時間、注入時間。同時也支援 watermark 機制來處理滞後資料。
容錯機制對于 Spark Streaming 任務,我們可以設定 checkpoint,然後假如發生故障并重新開機,我們可以從上次 checkpoint 之處恢複,但是這個行為隻能使得資料不丢失,可能會重複處理,不能做到恰好一次處理語義。Flink 則使用兩階段送出協定來解決這個問題。
根據 Flink 官網描述,Flink 是一個分層架構的系統,每一層所包含的元件都提供了特定的抽象,用來服務于上層元件。

自下而上,每一層分别代表:Deploy 層:該層主要涉及了Flink的部署模式,在上圖中我們可以看出,Flink 支援包括local、Standalone、Cluster、Cloud等多種部署模式 。
Runtime 層:Runtime層提供了支援 Flink 計算的核心實作,比如:支援分布式 Stream 處理、JobGraph到ExecutionGraph的映射、排程等等,為上層API層提供基礎服務 。
API層: API 層主要實作了面向流(Stream)處理和批(Batch)處理API,其中面向流處理對應DataStream API,面向批處理對應DataSet API,後續版本,Flink有計劃将DataStream和DataSet API進行統一 。
Libraries層: 該層稱為Flink應用架構層,根據API層的劃分,在API層之上建構的滿足特定應用的實作計算架構,也分别對應于面向流處理和面向批處理兩類。面向流處理支援:CEP(複雜事件處理)、基于SQL-like的操作(基于Table的關系操作);面向批處理支援:FlinkML(機器學習庫)、Gelly(圖處理)。
Flink可以完全獨立于Hadoop,在不依賴Hadoop元件下運作。但是做為大資料的基礎設施,Hadoop體系是任何大資料架構都繞不過去的。Flink可以內建衆多Hadooop 元件,例如Yarn、Hbase、HDFS等等。例如,Flink可以和Yarn內建做資源排程,也可以讀寫HDFS,或者利用HDFS做檢查點。
大家注意,這個問題看起來是問你實際應用中的Flink叢集規模,其實還隐藏着另一個問題:Flink可以支援多少節點的叢集規模?在回答這個問題時候,可以将自己生産環節中的叢集規模、節點、記憶體情況說明,同時說明部署模式(一般是Flink on Yarn),除此之外,使用者也可以同時在小叢集(少于5個節點)和擁有 TB 級别狀态的上千個節點上運作 Flink 任務。
上圖是來自Flink官網的運作流程圖。通過上圖我們可以得知,Flink 程式的基本建構是資料輸入來自一個 Source,Source 代表資料的輸入端,經過 Transformation 進行轉換,然後在一個或者多個Sink接收器中結束。資料流(stream)就是一組永遠不會停止的資料記錄流,而轉換(transformation)是将一個或多個流作為輸入,并生成一個或多個輸出流的操作。執行時,Flink程式映射到 streaming dataflows,由流(streams)和轉換操作(transformation operators)組成。
Flink 程式在運作時主要有 TaskManager,JobManager,Client 三種角色。
其中JobManager扮演着叢集中的管理者Master的角色,它是整個叢集的協調者,負責接收Flink Job,協調檢查點,Failover 故障恢複等,同時管理Flink叢集中從節點TaskManager。
TaskManager是實際負責執行計算的Worker,在其上執行Flink Job的一組Task,每個TaskManager負責管理其所在節點上的資源資訊,如記憶體、磁盤、網絡,在啟動的時候将資源的狀态向JobManager彙報。
Client是Flink程式送出的用戶端,當使用者送出一個Flink程式時,會首先建立一個Client,該Client首先會對使用者送出的Flink程式進行預處理,并送出到Flink叢集中處理,是以Client需要從使用者送出的Flink程式配置中擷取JobManager的位址,并建立到JobManager的連接配接,将Flink Job送出給JobManager。
在Flink架構角色中我們提到,TaskManager是實際負責執行計算的Worker,TaskManager 是一個 JVM 程序,并會以獨立的線程來執行一個task或多個subtask。為了控制一個 TaskManager 能接受多少個 task,Flink 提出了 Task Slot 的概念。簡單的說,TaskManager會将自己節點上管理的資源分為不同的Slot:固定大小的資源子集。這樣就避免了不同Job的Task互相競争記憶體資源,但是需要主要的是,Slot隻會做記憶體的隔離。沒有做CPU的隔離。
Flink 最常用的常用算子包括:<code>Map:DataStream → DataStream</code>,輸入一個參數産生一個參數,map的功能是對輸入的參數進行轉換操作。<code>Filter</code>:過濾掉指定條件的資料。<code>KeyBy</code>:按照指定的key進行分組。<code>Reduce</code>:用來進行結果彙總合并。<code>Window</code>:視窗函數,根據某些特性将每個key的資料進行分組(例如:在5s内到達的資料)
要搞懂什麼是分區政策,需要清楚分區政策是用來決定資料如何發送至下遊。目前 Flink 支援了8種分區政策的實作。
上圖是整個Flink實作的分區政策繼承圖:GlobalPartitioner 資料會被分發到下遊算子的第一個執行個體中進行處理。ShufflePartitioner 資料會被随機分發到下遊算子的每一個執行個體中進行處理。RebalancePartitioner 資料會被循環發送到下遊的每一個執行個體中進行處理。RescalePartitioner 這種分區器會根據上下遊算子的并行度,循環的方式輸出到下遊算子的每個執行個體。這裡有點難以了解,假設上遊并行度為2,編号為A和B。下遊并行度為4,編号為1,2,3,4。那麼A則把資料循環發送給1和2,B則把資料循環發送給3和4。假設上遊并行度為4,編号為A,B,C,D。下遊并行度為2,編号為1,2。那麼A和B則把資料發送給1,C和D則把資料發送給2。BroadcastPartitioner 廣播分區會将上遊資料輸出到下遊算子的每個執行個體中。适合于大資料集和小資料集做Jion的場景。ForwardPartitioner ForwardPartitioner 用于将記錄輸出到下遊本地的算子執行個體。它要求上下遊算子并行度一樣。簡單的說,ForwardPartitioner用來做資料的控制台列印。KeyGroupStreamPartitioner Hash分區器。會将資料按 Key 的 Hash 值輸出到下遊算子執行個體中。CustomPartitionerWrapper 使用者自定義分區器。需要使用者自己實作Partitioner接口,來定義自己的分區邏輯。例如:
Flink中的任務被分為多個并行任務來執行,其中每個并行的執行個體處理一部分資料。這些并行執行個體的數量被稱為并行度。我們在實際生産環境中可以從四個不同層面設定并行度:
操作算子層面(Operator Level)
執行環境層面(Execution Environment Level)
用戶端層面(Client Level)
系統層面(System Level)
需要注意的優先級:算子層面>環境層面>用戶端層面>系統層面。
官網上十分經典的圖:
slot是指 taskmanager 的并發執行能力,假設我們将 taskmanager.numberOfTaskSlots 配置為3 那麼每一個 taskmanager 中配置設定3個 TaskSlot, 3個 taskmanager 一共有9個TaskSlot。
parallelism是指taskmanager實際使用的并發能力。假設我們把 parallelism.default 設定為1,那麼9個 TaskSlot 隻能用1個,有8個空閑。
Flink 實作了多種重新開機政策。
固定延遲重新開機政策(Fixed Delay Restart Strategy)
故障率重新開機政策(Failure Rate Restart Strategy)
沒有重新開機政策(No Restart Strategy)
Fallback重新開機政策(Fallback Restart Strategy)
Flink實作的分布式緩存和Hadoop有異曲同工之妙。目的是在本地讀取檔案,并把他放在 taskmanager 節點中,防止task重複拉取。
我們知道Flink是并行的,計算過程可能不在一個 Slot 中進行,那麼有一種情況即:當我們需要通路同一份資料。那麼Flink中的廣播變量就是為了解決這種情況。我們可以把廣播變量了解為是一個公共的共享變量,我們可以把一個dataset 資料集廣播出去,然後不同的task在節點上都能夠擷取到,這個資料在每個節點上隻會存在一份。
說說Flink中的視窗?
Flink 支援兩種劃分視窗的方式,按照time和count。如果根據時間劃分視窗,那麼它就是一個time-window 。如果根據資料劃分視窗,那麼它就是一個 count-window。flink支援視窗的兩個重要屬性(size和interval)如果size=interval,那麼就會形成tumbling-window(無重疊資料) ;如果size>interval,那麼就會形成sliding-window(有重疊資料) 如果size< interval, 那麼這種視窗将會丢失資料。比如每5秒鐘,統計過去3秒的通過路口汽車的資料,将會漏掉2秒鐘的資料。通過組合可以得出四種基本視窗:
time-tumbling-window 無重疊資料的時間視窗,設定方式舉例:timeWindow(Time.seconds(5))
time-sliding-window 有重疊資料的時間視窗,設定方式舉例:timeWindow(Time.seconds(5), Time.seconds(3))
count-tumbling-window無重疊資料的數量視窗,設定方式舉例:countWindow(5)
count-sliding-window 有重疊資料的數量視窗,設定方式舉例:countWindow(5,3)
Flink在做計算的過程中經常需要存儲中間狀态,來避免資料丢失和狀态恢複。選擇的狀态存儲政策不同,會影響狀态持久化如何和 checkpoint 互動。
Flink提供了三種狀态存儲方式:MemoryStateBackend、FsStateBackend、RocksDBStateBackend。
Flink 中的時間和其他流式計算系統的時間一樣分為三類:事件時間,攝入時間,處理時間三種。如果以 <code>EventTime</code>為基準來定義時間視窗将形成EventTimeWindow,要求消息本身就應該攜帶EventTime。如果以<code>IngesingtTime</code> 為基準來定義時間視窗将形成 IngestingTimeWindow,以 source 的systemTime為準。如果以 <code>ProcessingTime</code> 基準來定義時間視窗将形成 ProcessingTimeWindow,以 operator 的systemTime 為準。
Watermark 是 Apache Flink 為了處理 EventTime 視窗計算提出的一種機制, 本質上是一種時間戳。 一般來講Watermark經常和Window一起被用來處理亂序事件。
TableEnvironment是Table API和SQL內建的核心概念。這個類主要用來:
在内部 catalog 中系統資料庫
注冊外部 catalog
執行SQL查詢
注冊使用者定義(标量,表或聚合)函數
将DataStream或DataSet轉換為表
持有對 ExecutionEnvironment 或 StreamExecutionEnvironment 的引用
首先大家要知道 Flink 的SQL解析是基于Apache Calcite這個開源架構。
基于此,一次完整的SQL解析過程如下:
使用者使用對外提供Stream SQL的文法開發業務應用
用calcite對StreamSQL進行文法檢驗,文法檢驗通過後,轉換成calcite的邏輯樹節點;最終形成calcite的邏輯計劃
采用Flink自定義的優化規則和calcite火山模型、啟發式模型共同對邏輯樹進行優化,生成最優的Flink實體計劃
對實體計劃采用janino codegen生成代碼,生成用低階API DataStream 描述的流應用,送出到Flink平台執行
Flink中級
本道面試題考察的其實就是一句話:Flink的開發者認為批處理是流處理的一種特殊情況。批處理是有限的流處理。Flink 使用一個引擎支援了DataSet API 和 DataStream API。
在一個Flink Job中,資料需要在不同的task中進行交換,整個資料交換是由 TaskManager 負責的,TaskManager 的網絡元件首先從緩沖buffer中收集records,然後再發送。Records 并不是一個一個被發送的,二是積累一個批次再發送,batch 技術可以更加高效的利用網絡資源。
Flink實作容錯主要靠強大的CheckPoint機制和State機制。Checkpoint 負責定時制作分布式快照、對程式中的狀态進行備份;State 用來存儲計算過程中的中間狀态。
Flink的分布式快照是根據Chandy-Lamport算法量身定做的。簡單來說就是持續建立分布式資料流及其狀态的一緻快照。
核心思想是在 input source 端插入 barrier,控制 barrier 的同步來實作 snapshot 的備份和 exactly-once 語義。
Flink通過實作兩階段送出和狀态儲存來實作端到端的一緻性語義。 分為以下幾個步驟:
開始事務(beginTransaction)建立一個臨時檔案夾,來寫把資料寫入到這個檔案夾裡面
預送出(preCommit)将記憶體中緩存的資料寫入檔案并關閉
若失敗發生在預送出成功後,正式送出前。可以根據狀态來送出預送出的資料,也可删除預送出的資料
Flink源碼中有一個獨立的connector子產品,所有的其他connector都依賴于此子產品,Flink 在1.9版本釋出的全新kafka連接配接器,摒棄了之前連接配接不同版本的kafka叢集需要依賴不同版本的connector這種做法,隻需要依賴一個connector即可。
Flink 并不是将大量對象存在堆上,而是将對象都序列化到一個預配置設定的記憶體塊上。此外,Flink大量的使用了堆外記憶體。如果需要處理的資料超出了記憶體限制,則會将部分資料存儲到硬碟上。Flink 為了直接操作二進制資料實作了自己的序列化架構。理論上Flink的記憶體管理分為三部分:
Network Buffers:這個是在TaskManager啟動的時候配置設定的,這是一組用于緩存網絡資料的記憶體,每個塊是32K,預設配置設定2048個,可以通過“taskmanager.network.numberOfBuffers”修改。
Memory Manage pool:大量的Memory Segment塊,用于運作時的算法(Sort/Join/Shuffle等),這部分啟動的時候就會配置設定。下面這段代碼,根據配置檔案中的各種參數來計算記憶體的配置設定方法。(heap or off-heap,這個放到下節談),記憶體的配置設定支援預配置設定和lazy load,預設懶加載的方式。
User Code,這部分是除了Memory Manager之外的記憶體用于User code和TaskManager本身的資料結構。
Java本身自帶的序列化和反序列化的功能,但是輔助資訊占用空間比較大,在序列化對象時記錄了過多的類資訊。Apache Flink摒棄了Java原生的序列化方法,以獨特的方式處理資料類型和序列化,包含自己的類型描述符,泛型類型提取和類型序列化架構。TypeInformation 是所有類型描述符的基類。它揭示了該類型的一些基本屬性,并且可以生成序列化器。TypeInformation 支援以下幾種類型:
BasicTypeInfo: 任意Java 基本類型或 String 類型
BasicArrayTypeInfo: 任意Java基本類型數組或 String 數組
WritableTypeInfo: 任意 Hadoop Writable 接口的實作類
CaseClassTypeInfo: 任意的 Scala CaseClass(包括 Scala tuples)
GenericTypeInfo: 任意無法比對之前幾種類型的類
針對前六種類型資料集,Flink皆可以自動生成對應的TypeSerializer,能非常高效地對資料集進行序列化和反序列化。
window産生資料傾斜指的是資料在不同的視窗内堆積的資料量相差過多。本質上産生這種情況的原因是資料源頭發送的資料量速度不同導緻的。出現這種情況一般通過兩種方式來解決:
在資料進入視窗前做預聚合
重新設計視窗聚合的key
資料傾斜和資料熱點是所有大資料架構繞不過去的問題。處理這類問題主要從3個方面入手:
在業務上規避這類問題
例如一個假設訂單場景,北京和上海兩個城市訂單量增長幾十倍,其餘城市的資料量不變。這時候我們在進行聚合的時候,北京和上海就會出現資料堆積,我們可以單獨資料北京和上海的資料。
Key的設計上
把熱key進行拆分,比如上個例子中的北京和上海,可以把北京和上海按照地區進行拆分聚合。
參數設定
Flink 1.9.0 SQL(Blink Planner) 性能優化中一項重要的改進就是更新了微批模型,即 MiniBatch。原理是緩存一定的資料後再觸發處理,以減少對State的通路,進而提升吞吐和減少資料的輸出量。
在Flink的背景任務管理中,我們可以看到Flink的哪個算子和task出現了反壓。最主要的手段是資源調優和算子調優。資源調優即是對作業中的Operator的并發數(parallelism)、CPU(core)、堆記憶體(heap_memory)等參數進行調優。作業參數調優包括:并行度的設定,State的設定,checkpoint的設定。
Flink 内部是基于 producer-consumer 模型來進行消息傳遞的,Flink的反壓設計也是基于這個模型。Flink 使用了高效有界的分布式阻塞隊列,就像 Java 通用的阻塞隊列(BlockingQueue)一樣。下遊消費者消費變慢,上遊就會受到阻塞。
Storm 是通過監控 Bolt 中的接收隊列負載情況,如果超過高水位值就會将反壓資訊寫到 Zookeeper ,Zookeeper 上的 watch 會通知該拓撲的所有 Worker 都進入反壓狀态,最後 Spout 停止發送 tuple。Flink中的反壓使用了高效有界的分布式阻塞隊列,下遊消費變慢會導緻發送端阻塞。二者最大的差別是Flink是逐級反壓,而Storm是直接從源頭降速。
為了更高效地分布式執行,Flink會盡可能地将operator的subtask連結(chain)在一起形成task。每個task在一個線程中執行。将operators連結成task是非常有效的優化:它能減少線程之間的切換,減少消息的序列化/反序列化,減少資料在緩沖區的交換,減少了延遲的同時提高整體的吞吐量。這就是我們所說的算子鍊。
支援hive讀寫,支援UDF
Flink SQL TopN和GroupBy等優化
Checkpoint跟savepoint針對實際業務場景做了優化
Flink state查詢
可以在處理前加一個fliter算子,将不符合規則的資料過濾出去。
Flink進階
使用者送出的Flink Job會被轉化成一個DAG任務運作,分别是:StreamGraph、JobGraph、ExecutionGraph,Flink中JobManager與TaskManager,JobManager與Client的互動是基于Akka工具包的,是通過消息驅動。整個Flink Job的送出還包含着ActorSystem的建立,JobManager的啟動,TaskManager的啟動和注冊。
一個Flink任務的DAG生成計算圖大緻經曆以下三個過程:
StreamGraph 最接近代碼所表達的邏輯層面的計算拓撲結構,按照使用者代碼的執行順序向StreamExecutionEnvironment添加StreamTransformation構成流式圖。
JobGraph 從StreamGraph生成,将可以串聯合并的節點進行合并,設定節點之間的邊,安排資源共享slot槽位和放置相關聯的節點,上傳任務所需的檔案,設定檢查點配置等。相當于經過部分初始化和優化處理的任務圖。
ExecutionGraph 由JobGraph轉換而來,包含了任務具體執行所需的内容,是最貼近底層實作的執行圖。
JobManager 負責整個 Flink 叢集任務的排程以及資源的管理,從用戶端中擷取送出的應用,然後根據叢集中 TaskManager 上 TaskSlot 的使用情況,為送出的應用配置設定相應的 TaskSlot 資源并指令 TaskManager 啟動從用戶端中擷取的應用。JobManager 相當于整個叢集的 Master 節點,且整個叢集有且隻有一個活躍的 JobManager ,負責整個叢集的任務管理和資源管理。JobManager 和 TaskManager 之間通過 Actor System 進行通信,擷取任務執行的情況并通過 Actor System 将應用的任務執行情況發送給用戶端。同時在任務執行的過程中,Flink JobManager 會觸發 Checkpoint 操作,每個 TaskManager 節點 收到 Checkpoint 觸發指令後,完成 Checkpoint 操作,所有的 Checkpoint 協調過程都是在 Fink JobManager 中完成。當任務完成後,Flink 會将任務執行的資訊回報給用戶端,并且釋放掉 TaskManager 中的資源以供下一次送出任務使用。
JobManager的職責主要是接收Flink作業,排程Task,收集作業狀态和管理TaskManager。它包含一個Actor,并且做如下操作:
RegisterTaskManager: 它由想要注冊到JobManager的TaskManager發送。注冊成功會通過AcknowledgeRegistration消息進行Ack。
SubmitJob: 由送出作業到系統的Client發送。送出的資訊是JobGraph形式的作業描述資訊。
CancelJob: 請求取消指定id的作業。成功會傳回CancellationSuccess,否則傳回CancellationFailure。
UpdateTaskExecutionState: 由TaskManager發送,用來更新執行節點(ExecutionVertex)的狀态。成功則傳回true,否則傳回false。
RequestNextInputSplit: TaskManager上的Task請求下一個輸入split,成功則傳回NextInputSplit,否則傳回null。
JobStatusChanged: 它意味着作業的狀态(RUNNING, CANCELING, FINISHED,等)發生變化。這個消息由ExecutionGraph發送。
TaskManager 相當于整個叢集的 Slave 節點,負責具體的任務執行和對應任務在每個節點上的資源申請和管理。用戶端通過将編寫好的 Flink 應用編譯打包,送出到 JobManager,然後 JobManager 會根據已注冊在 JobManager 中 TaskManager 的資源情況,将任務配置設定給有資源的 TaskManager節點,然後啟動并運作任務。TaskManager 從 JobManager 接收需要部署的任務,然後使用 Slot 資源啟動 Task,建立資料接入的網絡連接配接,接收資料并開始資料處理。同時 TaskManager 之間的資料互動都是通過資料流的方式進行的。可以看出,Flink 的任務運作其實是采用多線程的方式,這和 MapReduce 多 JVM 進行的方式有很大的差別,Flink 能夠極大提高 CPU 使用效率,在多個任務和 Task 之間通過 TaskSlot 方式共享系統資源,每個 TaskManager 中通過管理多個 TaskSlot 資源池進行對資源進行有效管理。
TaskManager的啟動流程較為簡單:
啟動類:<code>org.apache.flink.runtime.taskmanager.TaskManager</code>
核心啟動方法 : <code>selectNetworkInterfaceAndRunTaskManager</code>
啟動後直接向<code>JobManager</code>注冊自己,注冊完成後,進行部分子產品的初始化。
TaskManager中最細粒度的資源是Task slot,代表了一個固定大小的資源子集,每個TaskManager會将其所占有的資源平分給它的slot。
通過調整 task slot 的數量,使用者可以定義task之間是如何互相隔離的。每個 TaskManager 有一個slot,也就意味着每個task運作在獨立的 JVM 中。每個 TaskManager 有多個slot的話,也就是說多個task運作在同一個JVM中。
Flink 為了避免JVM的固有缺陷例如java對象存儲密度低,FGC影響吞吐和響應等,實作了自主管理記憶體。MemorySegment就是Flink的記憶體抽象。預設情況下,一個MemorySegment可以被看做是一個32kb大的記憶體塊的抽象。這塊記憶體既可以是JVM裡的一個byte[],也可以是堆外記憶體(DirectByteBuffer)。在MemorySegment這個抽象之上,Flink在資料從operator内的資料對象在向TaskManager上轉移,預備被發給下個節點的過程中,使用的抽象或者說記憶體對象是Buffer。對接從Java對象轉為Buffer的中間對象是另一個抽象StreamRecord。
Flink的容錯機制的核心部分是制作分布式資料流和操作算子狀态的一緻性快照。 這些快照充當一緻性checkpoint,系統可以在發生故障時復原。 Flink用于制作這些快照的機制在“分布式資料流的輕量級異步快照”中進行了描述。 它受到分布式快照的标準Chandy-Lamport算法的啟發,專門針對Flink的執行模型而定制。
barriers在資料流源處被注入并行資料流中。快照n的barriers被插入的位置(我們稱之為Sn)是快照所包含的資料在資料源中最大位置。例如,在Apache Kafka中,此位置将是分區中最後一條記錄的偏移量。 将該位置Sn報告給checkpoint協調器(Flink的JobManager)。然後barriers向下遊流動。當一個中間操作算子從其所有輸入流中收到快照n的barriers時,它會為快照n發出barriers進入其所有輸出流中。 一旦sink操作算子(流式DAG的末端)從其所有輸入流接收到barriers n,它就向checkpoint協調器确認快照n完成。在所有sink确認快照後,意味快照着已完成。一旦完成快照n,job将永遠不再向資料源請求Sn之前的記錄,因為此時這些記錄(及其後續記錄)将已經通過整個資料流拓撲,也即是已經被處理結束。
Flink 将 SQL 校驗、SQL 解析以及 SQL 優化交給了Apache Calcite。Calcite 在其他很多開源項目裡也都應用到了,譬如 Apache Hive, Apache Drill, Apache Kylin, Cascading。Calcite 在新的架構中處于核心的地位,如下圖所示。
建構抽象文法樹的事情交給了 Calcite 去做。SQL query 會經過 Calcite 解析器轉變成 SQL 節點樹,通過驗證後建構成 Calcite 的抽象文法樹(也就是圖中的 Logical Plan)。另一邊,Table API 上的調用會建構成 Table API 的抽象文法樹,并通過 Calcite 提供的 RelBuilder 轉變成 Calcite 的抽象文法樹。然後依次被轉換成邏輯執行計劃和實體執行計劃。在送出任務後會分發到各個 TaskManager 中運作,在運作時會使用 Janino 編譯器編譯代碼後運作。
小結
本篇從Flink初級,再到中級,以及最後的Flink進階難度,對于Flink的常見面試題算是做了一個梳理。
想在大資料領域紮根,有所建樹的朋友可不能忽略對于Flink的"研究",作為未來大資料發展的大勢所趨,Flink勢必會更加的“火熱” !!!
看完還對Flink意猶未盡的朋友,可以嘗試去閱讀Flink的權威知識圖譜,關注“猿人菌”背景回複“Flink知識圖譜”即可獲得,更有更多部落客為大家準備的“大資料幹糧”,随時準備上車,期待您的加入!
一鍵三連,養成習慣~
文章持續更新,可以微信搜一搜「 猿人菌 」第一時間閱讀,思維導圖,大資料書籍,大資料高頻面試題,海量一線大廠面經,300G大資料全套視訊等你擷取…期待您的關注!
猜你喜歡
大資料面試殺招——Hive高頻考點,就怕你都會!
大資料面試殺招——Hadoop高頻考點,正在重新整理你的認知!
大資料面試殺招——Spark高頻考點,必知必會!