Flink 状态管理:算子状态、键值分区状态、状态后端、有状态算子的扩缩容
文章目錄
- 狀態(tài)管理
- 算子狀態(tài)
- 鍵值分區(qū)狀態(tài)
- 狀態(tài)后端(State Backends)
- 有狀態(tài)算子的擴(kuò)縮容
狀態(tài)管理
通常意義上,函數(shù)里所有需要任務(wù)去維護(hù)并用來計(jì)算結(jié)果的數(shù)據(jù)都屬于任務(wù)的狀態(tài),可以把狀態(tài)想象成任務(wù)的業(yè)務(wù)邏輯所需要訪問的本地或?qū)嵗兞?/strong>。
任務(wù)和狀態(tài)之間的經(jīng)典交互過程如上圖,任務(wù)首先會接受一些輸入數(shù)據(jù)。在處理這些數(shù)據(jù)的過程中,任務(wù)對其狀態(tài)進(jìn)行讀取或更新,并根據(jù)狀態(tài)的輸入數(shù)據(jù)計(jì)算結(jié)果。我們以一個(gè)持續(xù)計(jì)算接收到多少條記錄的簡單任務(wù)為例。當(dāng)任務(wù)收到一個(gè)新的記錄后,首先會訪問狀態(tài)獲取當(dāng)前統(tǒng)計(jì)的記錄數(shù)目,然后把數(shù)目增加并更新狀態(tài),最后將更新后的狀態(tài)數(shù)目發(fā)送出去。
Flink會負(fù)責(zé)進(jìn)行狀態(tài)的管理,包括狀態(tài)一致性、故障處理以及高效存取相關(guān)的問題都由Flink負(fù)責(zé)搞定,這樣開發(fā)人員就可以專注于自己的應(yīng)用邏輯。
在Flink中,狀態(tài)都是和特定operator(算子)相關(guān)聯(lián),為了讓Flink的Runtime(運(yùn)行)層知道算子有哪些狀態(tài),算子需要自己對其進(jìn)行注冊。根據(jù)作用域的不同,狀態(tài)可以分為以下兩類
- operator state(算子狀態(tài))
- keyed state(鍵值分區(qū)狀態(tài))
算子狀態(tài)
算子狀態(tài)的作用域是某個(gè)算子任務(wù),這意味著所有在同一個(gè)并行任務(wù)之內(nèi)的記錄都能訪問到相同的狀態(tài)==(每一個(gè)并行的子任務(wù)都共享一個(gè)狀態(tài))。算子狀態(tài)不能通過其他任務(wù)訪問,無論該任務(wù)是否來自相同算子(相同算子的不同任務(wù)之間也不能訪問)==。
帶有算子狀態(tài)的任務(wù)Flink為算子狀態(tài)提供了三種數(shù)據(jù)結(jié)構(gòu)
鍵值分區(qū)狀態(tài)
鍵值分區(qū)狀態(tài)會按照算子輸入記錄所定義的鍵值來進(jìn)行維護(hù)或訪問。Flink為每個(gè)鍵值都維護(hù)了一個(gè)狀態(tài)實(shí)例,該實(shí)例總是位于那個(gè)處理對應(yīng)鍵值記錄的算子任務(wù)上。當(dāng)任務(wù)在處理一個(gè)記錄時(shí),會自動(dòng)把狀態(tài)的訪問范圍限制為當(dāng)前記錄的鍵值,因此所有鍵值相同的記錄都能訪問到一樣的狀態(tài)。
帶有鍵值分區(qū)狀態(tài)的任務(wù)Flink為鍵值分區(qū)狀態(tài)提供以下幾種數(shù)據(jù)結(jié)構(gòu)
狀態(tài)后端(State Backends)
有狀態(tài)算子的任務(wù)通常會對每一條到來的記錄讀寫狀態(tài),因此高效的狀態(tài)訪問對于記錄處理的低延遲而言至關(guān)重要。為了保證快速訪問狀態(tài),每個(gè)并行任務(wù)都會把狀態(tài)維護(hù)在本地。至于狀態(tài)具體的存儲、訪問和維護(hù),則是由一個(gè)名為狀態(tài)后端的**可拔插(pluggable)**組件來決定。狀態(tài)后端主要負(fù)責(zé)兩件事情:本地狀態(tài)管理和將狀態(tài)以檢查點(diǎn)的形式寫入遠(yuǎn)程存儲。
目前,Flink提供了三種狀態(tài)后端,狀態(tài)后端的選擇會影響有狀態(tài)應(yīng)用的魯棒性及性能。
MemoryStateBackend
- MemoryStateBackend將狀態(tài)以常規(guī)對象的方式存儲在TaskManager進(jìn)程的JVM堆,并在生成Checkpoints時(shí)會將狀態(tài)發(fā)送至JobManager并保存到它的堆內(nèi)存中。
- 如果狀態(tài)過大,則可能導(dǎo)致JVM上的任務(wù)由于OutOfMemoryError而終止,并且可能由于堆中放置了過多常駐內(nèi)存的對象而引發(fā)垃圾回收停頓問題。
- 由于內(nèi)存具有易失性,所以一旦JobManager出現(xiàn)故障就會導(dǎo)致狀態(tài)丟失,因此MemoryStateBackend通常用于開發(fā)和調(diào)試。
- 內(nèi)存訪問速度快,延遲低,但容錯(cuò)性也低。
FsStateBackend
- 與MemoryStateBackend一樣將本地狀態(tài)存儲在TaskManager進(jìn)程的JVM堆里,不同的是將Checkpoints存到了遠(yuǎn)程持久化文件系統(tǒng)(FileSystem)中。
- 受到TaskManager內(nèi)存大小的限制,并且也可能導(dǎo)致垃圾回收停頓問題。
- FsStateBackend既讓本地訪問享有內(nèi)存的速度,又可以支持故障容錯(cuò)。
RocksDBStateBackend
-
RocksDBStateBackend會將全部狀態(tài)序列化后存到本地RocksDB實(shí)例中
-
由于磁盤I/O以及序列化/反序列化對象的性能開銷,相較于內(nèi)存中維護(hù)狀態(tài)而言, 讀寫性能會偏低。
-
RocksDB的支持并不直接包含在Flink中,需要額外引入依賴
<dependency> <groupId>org.apache.flink</groupId> <artifactId>flink-statebackend-rocksdb_2.12</artifactId> <version>1.12.1</version> </dependency>
有狀態(tài)算子的擴(kuò)縮容
流式應(yīng)用的一項(xiàng)基本需求是根據(jù)輸入數(shù)據(jù)到達(dá)速率的變化調(diào)整算子的并行度。對于無狀態(tài)的算子擴(kuò)縮容很容易,但是對于有狀態(tài)算子來說,這就變的復(fù)雜了很多。因?yàn)槲覀?strong>需要把狀態(tài)重新分組,分配到與之前數(shù)量不等的并行任務(wù)上。
針對不同類型狀態(tài)的算子,Flink提供了四種擴(kuò)縮容模式
鍵值分區(qū)狀態(tài)
帶有鍵值分區(qū)狀態(tài)的算子在擴(kuò)縮容時(shí)會根據(jù)新的任務(wù)數(shù)量對鍵值重新分區(qū),但為了降低狀態(tài)在不同任務(wù)之間遷移的必要成本,Flink不會對單獨(dú)的鍵值實(shí)施再分配,而是會把所有鍵值分為不同的鍵值組(Key group)。每個(gè)鍵值組都包含了部分鍵值,Flink以此為單位把鍵值分配給不同任務(wù)。
算子擴(kuò)縮容時(shí)鍵值分區(qū)狀態(tài)的調(diào)整算子列表狀態(tài)
帶有算子列表狀態(tài)的算子在擴(kuò)縮容時(shí)會對列表中的條目進(jìn)行重新分配。理論上,所有并行算子任務(wù)的列表?xiàng)l目會被統(tǒng)一收集起來,隨后均勻分配到更少或更多的任務(wù)之上。如果列表?xiàng)l目的數(shù)量小于算子新設(shè)置的并行度,部分任務(wù)在啟動(dòng)時(shí)的狀態(tài)就可能為空。
算子擴(kuò)縮容時(shí)算子列表狀態(tài)的調(diào)整算子聯(lián)合列表狀態(tài)
帶有算子聯(lián)合列表狀態(tài)的算子會在擴(kuò)縮容時(shí)把狀態(tài)列表的全部條目廣播到全部任務(wù)上,隨后由任務(wù)自己決定哪些條目應(yīng)該保留,哪些應(yīng)該丟棄。
算子擴(kuò)縮容時(shí)算子聯(lián)合列表狀態(tài)的調(diào)整算子廣播狀態(tài)
帶有算子廣播狀態(tài)的算子在擴(kuò)縮容時(shí)會把狀態(tài)拷貝到全部新任務(wù)上,這樣做的原因是廣播狀態(tài)能確保所有任務(wù)的狀態(tài)相同。在縮容的情況下,由于狀態(tài)經(jīng)過復(fù)制不會丟失,我們可以簡單的停掉多出的任務(wù)。
算子擴(kuò)縮容時(shí)算子廣播狀態(tài)的調(diào)整總結(jié)
以上是生活随笔為你收集整理的Flink 状态管理:算子状态、键值分区状态、状态后端、有状态算子的扩缩容的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Flink 时间语义与水位线(Water
- 下一篇: Flink 容错机制:Checkpoin