淺談Flink基于RocksDB的增量檢查點(incremental checkpoint)機制

Intro

Flink之所以能夠做到高效而準確的有狀態流式處理,核心是依賴于檢查點(checkpoint)機制。當流式程序運行出現異常時,能夠從最近的一個檢查點恢復,從而最大限度地保證數據不丟失也不重復。

Flink檢查點本質上是通過異步屏障快照(asychronous barrier snapshot, ABS)算法產生的全局狀態快照,一般是存儲在分布式文件系統(如HDFS)上。但是,如果狀態空間超大(比如key非常多或者窗口區間很長),檢查點數據可能會達到GB甚至TB級別,每次做checkpoint都會非常耗時。但是,海量狀態數據在檢查點之間的變化往往沒有那么明顯,增量檢查點可以很好地解決這個問題。顧名思義,增量檢查點只包含本次checkpoint與上次checkpoint狀態之間的差異,而不是所有狀態,變得更加輕量級了。

From https://www.slideshare.net/FlinkForward/stephan-ewen-scaling-to-large-state

Incremental CP on RocksDB Backend

目前Flink有3種狀態后端,即內存(MemoryStateBackend)、文件系統(FsStateBackend)和RocksDB(RocksDBStateBackend),只有RocksDB狀態后端支持增量檢查點。該功能默認關閉,要打開它可以在flink-conf.yaml中配置:

state.backend: rocksdb
state.backend.incremental: true

或者在代碼中配置:

RocksDBStateBackend rocksDBStateBackend = new RocksDBStateBackend("hdfs://path/to/flink-checkpoints", true);
env.setStateBackend(rocksDBStateBackend);

為什么只有RocksDB狀態后端支持增量檢查點呢?這是由RocksDB本身的特性決定的。RocksDB是一個基于日志結構合并樹(LSM樹)的鍵值式存儲引擎,它可以視為HBase等引擎的思想基礎,故與HBase肯定有諸多相似之處。如果看官不了解LSM樹的話,可以通過筆者之前寫的這篇文章來做個簡單的了解。

在RocksDB中,扮演讀寫緩存的角色叫做memtable。memtable寫滿之后會flush到磁盤形成數據文件,叫做sstable(是“有序序列表”即sorted sequence table的縮寫)。RocksDB也存在compaction策略,在后臺合并已經寫入的sstable,原有的sstable會包含所有的鍵值對,合并前的sstable在此后會被刪除。關于compaction,筆者寫了一篇非常詳細的文章來探討,見這里

在啟用RocksDB狀態后端后,Flink的每個checkpoint周期都會記錄RocksDB庫的快照,并持久化到文件系統中。所以RocksDB的預寫日志(WAL)機制可以安全地關閉,沒有重放數據的必要性了。

From https://www.slideshare.net/dataArtisans/webinar-deep-dive-on-apache-flink-state-seth-wiesman

Illustrating Incremental CP

有了上面的鋪墊,下面通過例子來解釋增量檢查點的過程。

From https://flink.apache.org/features/2018/01/30/incremental-checkpointing.html

上圖示出一個有狀態的算子的4個檢查點,其ID為2,并且state.checkpoints.num-retained參數設為2,表示保留2個檢查點。表格中的4列分別表示RocksDB中的sstable文件,sstable文件與存儲系統中文件路徑的映射,sstable文件的引用計數,以及保留的檢查點的范圍。

下面按部就班地解釋一下:

  1. 檢查點CP 1完成后,產生了兩個sstable文件,即sstable-(1)與sstable-(2)。這兩個文件會寫到持久化存儲(如HDFS),并將它們的引用計數記為1。
  2. 檢查點CP 2完成后,新增了兩個sstable文件,即sstable-(3)與sstable-(4),這兩個文件的引用計數記為1。并且由于我們要保留2個檢查點,所以上一步CP 1產生的兩個文件也要算在CP 2內,故sstable-(1)與sstable-(2)的引用計數會加1,變成2。
  3. 檢查點CP 3完成后,RocksDB的compaction線程將sstable-(1)、sstable-(2)、sstable-(3)三個文件合并成了一個文件sstable-(1,2,3)。CP 2產生的sstable-(4)得以保留,引用計數變為2,并且又產生了新的sstable-(5)文件。注意此時CP 1已經過期,所以sstable-(1)、sstable-(2)兩個文件不會再被引用,引用計數減1。
  4. 檢查點CP 4完成后,RocksDB的compaction線程將sstable-(4)、sstable-(5)以及新生成的sstable-(6)三個文件合并成了sstable-(4,5,6),并對sstable-(1,2,3)、sstable-(4,5,6)引用加1。由于CP 2也過期了,所以sstable-([1~4])四個文件的引用計數同時減1,這就造成sstable-(1)、sstable-(2)、sstable-(3)的引用計數變為0,Flink就從存儲系統中刪除掉這三個文件。

通過上面的分析,我們可以看出Flink增量檢查點機制的巧妙之處:

  • 通過跟蹤sstable的新增和刪除,可以記錄狀態數據的變化;
  • 通過引用計數的方式,上一個檢查點中已經存在的文件可以直接被引用,不被引用的文件可以及時刪除;
  • 可以保證當前有效的檢查點都不引用已經刪除的文件,從而保留state.checkpoints.num-retained參數指定的狀態歷史。

What to Concern...

增量檢查點解決了大狀態checkpointing的問題,但是在從檢查點恢復現場時會帶來潛在的overhead。這是顯然的:當程序出問題后,TaskManager需要從多個檢查點中加載狀態數據,并且這些數據中還可能會包含將被刪除的狀態。

還有一點,就算磁盤空間緊張,舊檢查點的文件也不能隨便刪除,因為新檢查點仍然會引用它們,如果貿然刪除,程序就無法恢復現場了。這就提示我們,如果狀態本身的數據量不大,并且狀態之間的overlap也不明顯的話,開啟增量檢查點可能會造成反效果(checkpoint數據量異常膨脹),所以應該按需使用。

The End

明天還要繼續搬磚,民那晚安。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 1 概述 Apache Flink是可以進行有狀態的流處理,然而,在流處理中什么是狀態呢?狀態是有過去事件的在內存...
    薛定諤的貓Plus閱讀 3,216評論 0 1
  • 最近看了看Flink中state方面的知識,Flink中的state是啥?state的作用是啥?為什么Flink中...
    MrSocean閱讀 7,094評論 3 13
  • 了解Flink是什么,Flink應用程序運行的多樣化,對比業界常用的流處理框架,Flink的發展趨勢,Flink生...
    JavaEdge閱讀 5,086評論 1 18
  • 單行線的秘密揭開: 那是我獨行 沒有回頭路的紅蓋頭 我把柔弱,盤成發髻 渾身經歷過的荊棘,拔下來 插在頭上,變成精...
    二兩酒仙閱讀 606評論 10 12
  • 荒廢了好幾天沒有寫,今天是教師節,心里有好多話想對老師們說,也借著今天重新再繼續寫下去 常聽說,遇到一個好老師很難...
    子小心閱讀 556評論 0 0