有狀態計算是指在程序計算過程中,在Flink程序內部存儲計算產生的中間結果,並提供給後續Function或者算子計算結果使用。
狀態數據可以維繫在本地存儲中(Flink的堆內存或者堆外存),也可以藉助第三方的存儲,例如Flink已經實現的RocksDB,或者自定義其他存儲。
state 可以理解爲Flink上下文中可以access的一個內存數據庫(相比於無狀態計算,需要實現同樣功能需要藉助外部數據庫,如Redis),通過存取更新狀態,從而實現有狀態地算子運算。
案例:
- 用戶想實現CEP(複雜事件處理),獲取符合某一特定事件規則的事件,狀態計算就可以將接入的事件進行存儲,然後等待符合規則的事件觸發;
- 用戶想按照分鐘、小時、天進行聚合計算,求取當前的最大值、均值等聚合指標,這就需要利用狀態來維護當前計算過程中產生的結果,例如事件的總數、總和以及最大,最小值等
在Flink中需要通過創建StateDescriptor來獲取相應State的操作類。StateDescriptor主要定義了狀態的名稱、狀態中數據的類型參數信息以及狀態自定義函數。每種Managed Keyed State有相應的StateDescriptor,例如ValueStateDescriptor、ListStateDescriptor、ReducingState-Descriptor、FoldingStateDescriptor、MapStateDescriptor等
其中獲取狀態的方式是在RichFunction的Open方法中,以ValueState爲例:
private var leastValueState: ValueState[Long] = _
override def open(parameters: Configuration): Unit = {
//創建ValueStateDescriptor,定義狀態名稱爲leastValue,並指定數據類型
val leastValueStateDescriptor = new ValueStateDescriptor[Long]("leastValue", classOf[Long])
//通過getRuntimeContext.getState獲取State
leastValueState = getRuntimeContext.getState(leastValueStateDescriptor)
}
根據是否按key分區,分爲Keyed State 和 Operator State
意思是:
(1)keyed state 和 Key以及具體的Operator 綁定。
(2)Operator State 只和Operator算子有關。
根據是否託管Flink自動狀態管理,分爲Managed State 和 Raw State
Managed State 直接將狀態管理交給Flink,checkpoint時存儲的是bytes數據。
託管的好處是,Flink可以更好的支持狀態數據的重平衡以及更加完善的內存管理。
Managed Keyed State
狀態名 | 解釋 | 獲取值 | 更新值 |
---|---|---|---|
ValueState[T] | 與Key對應的單個值狀態 | T .value() | .update(T) |
ListState[T] | 與Key對應的列表的狀態 | get(i) | .add .addAll .update |
ReducingState[T] | 單個聚合值的狀態, 每次調用add 方法添加值的時候,會調用reduceFunction,最後合併到一個單一的狀態值。 |
T get() | add(T) |
AggregatingState[IN,OUT] | 與ReducingState唯一的區別是,輸入輸出類型可以不同。 | OUT get() | add(IN |
MapState[UK,UV] | 定義與Key對應的鍵值對的狀態 | get(UK) | .put .putAll .entries() .keys() .values() |
Keyed State 生命週期
對於任何類型Keyed State都可以設定狀態的生命週期(TTL),以確保能夠在規定時間內及時地清理狀態數據。狀態生命週期功能可以通過StateTtlConfig配置,然後將StateTtlConfig配置傳入StateDescriptor中的enableTimeToLive方法中即可。
//創建StateTtlConfig
val stateTtlConfig = StateTtlConfig
//指定TTL時長爲10s
.newBuilder(Time.seconds(10))
//指定TTL刷新時只對創建和寫入操作有效
.setUpdateType(StateTtlConfig.UpdateType.OnCreateAndWrite)
//指定狀態可見性爲永遠不反悔過期數據
.setStateVisibility(StateTtlConfig.StateVisibility.NeverReturnExpired)
.build
//創建ValueStateDescriptor
val valueStateDescriptor = new ValueStateDescriptor[String]("valueState", classOf[Long])
//指定創建好的stateTtlConfig
valueStateDescriptor.enableTimeToLive(stateTtlConfig)
Managed Operator State
應用場景,例如:
統計算子輸入數據量
維護Topics分區和Offsets偏移量
案例,統計算子輸入數據量:
class numberRecordsCount extends FlatMapFunction[(String, Long), (String,
Long)] with ListCheckpointed[Long] {
//定義算子中接入的numberRecords數量
private var numberRecords: Long = 0L
override def flatMap(t: (String, Long), collector: Collector[(String, Long)]): Unit = {
//接入一條記錄則進行統計,並輸出
numberRecords += 1
collector.collect(t._1, numberRecords)
}
override def snapshotState(checkpointId: Long, ts: Long): util.List[Long] = {
//Snapshot狀態的過程中將numberRecords寫入
Collections.singletonList(numberRecords)
}
override def restoreState(list: util.List[Long]): Unit = {
numberRecords = 0L
for (count <- list) {
//從狀態中恢復numberRecords數據
numberRecords += count }}}