flink上下游並行度不一致導致的資料亂序問題

耗子哥信徒發表於2024-08-19

問題描述

SingleOutputStreamOperator<Row> aggregatedStream = patrolStream
                .union(timerGarbageStream)
                .filter(v -> v.getFacility() != null && (v.getFacility().getType() == 11 || v.getFacility().getType() == 48))
                .setParallelism(12)
                .map(x -> (PatrolRecord) x)
                .setParallelism(12)
                .filter(x -> x.getInOutState() != 2)
                .setParallelism(12)
                .keyBy(v -> (Integer) v.getFacility().getId())
                .window(TumblingEventTimeWindows.of(Time.seconds(86400), Time.seconds(-Constants.offsetSeconds)))
                .allowedLateness(Constants.allowedLateness)
                .trigger(CountAndEventTimeTrigger.of(1L))
                .aggregate(new GarbageReduceFunction(), new GarbageProcessWindowFunction())
                .setParallelism(12)
                .returns(rowTypeInfo);

image
運算元A(紅色)往側流中寫入資料:

{"s":"2024-08-19 07:16:36","e":"2024-08-19 07:19:36","w":350000,"inferred":false,"rk":"DELETE","f_id":549737,"f_type":11,"inOutState":0,"loadCount":19}
{"s":"2024-08-19 07:16:36","e":"2024-08-19 07:19:36","w":318181,"inferred":false,"rk":"INSERT","f_id":549737,"f_type":11,"inOutState":0,"loadCount":19}

e是事件時間,相同的key、相同的事件時間,寫入程式一定是先寫入RowKind為DELETE的資料,後寫入RowKind為INSERT的資料。

下游運算元C(右下角)從側流中消費資料,並寫入DB。
問題在於,消費到的資料,部分資料的順序亂了,先消費到RowKind為INSERT的資料,後消費到RowKind為DELETE的資料。

問題分析

運算元A(紅色)的並行度是16
運算元B(中間的filter)並行度是12
運算元C(右下角)並行度是12

從A到B,進行了rebalance

rebalance 是一個資料重分佈(redistribution)操作,它將上游運算元的輸出資料重新分配到下游的並行子任務中。rebalance 透過迴圈(round-robin)的方式將資料均勻地分配到所有下游任務槽(task slot)上,從而實現負載均衡。

也就是說,這一步一定存在打亂順序的可能性。可能出現的結果:

key1、eventTime1、DELETE => 子任務1
key1、eventTime1、INSERT => 子任務2

本來連續的兩行資料,被路由到兩個不同的運算元。

接下來從運算元B到運算元C,執行的是HASH分割槽:

hash 是一種分割槽策略,用於將資料根據其 key 的雜湊值分配到不同的並行任務槽(task slot)上。這種分割槽方式可以確保相同的 key 始終被分配到同一個任務槽中,從而實現資料的分組操作。

此時,儘管key1一定會被路由到同一個子任務上,但是順序已經無法保證了。

接下來把所有運算元並行度都修改為12,看下效果

image

這時候從A到B,執行的是forward分割槽策略。

forward 是一種分割槽策略,用於將資料從上游運算元直接傳遞到下游運算元的對應並行例項中,而不進行任何重新分割槽或重新分配。也就是說,forward 保證了資料會直接從上游的一個並行子任務傳遞到下游的同一個並行子任務。

接下來再執行HASH分割槽,因此呢,同一個KEY的資料的相對順序並沒有改變。

結論

雖然驗證結果還沒出來,但是大機率這就是答案了。

相關文章