如何設(shè)計事件流,第 4 部分
在之前部分中,我們從宏觀角度考慮了我們的數(shù)據(jù),并區(qū)分了內(nèi)部數(shù)據(jù)和外部數(shù)據(jù)。我們還討論了模式和數(shù)據(jù)契約,以及它們?nèi)绾翁峁╇S著時間的推移協(xié)商、更改和發(fā)展我們的流的方法。最后,我們介紹了事實(狀態(tài))和增量事件類型。事實事件最適合通信狀態(tài)和解耦系統(tǒng),而 Delta 事件往往更多地用于內(nèi)部數(shù)據(jù),例如在事件溯源和其他緊密耦合的用例中。
規(guī)范化表創(chuàng)建規(guī)范化流
規(guī)范化的表導(dǎo)致規(guī)范化的事件流。連接器(例如,CDC)將數(shù)據(jù)直接從數(shù)據(jù)庫拉入事件流的鏡像集。這并不理想,因為它在內(nèi)部數(shù)據(jù)庫表和外部事件流之間創(chuàng)建了強耦合。
考慮一個簡單的電子商務(wù)項目及其關(guān)聯(lián)的品牌和稅收狀態(tài)表。
品牌和稅收狀態(tài)表通過外鍵關(guān)系與項目表相關(guān)。雖然我們只在表中顯示一項,但您可能會有數(shù)千(或數(shù)百萬)項,具體取決于您銷售的產(chǎn)品。
通常為每個表設(shè)置一個連接器,從表中提取數(shù)據(jù),將其組合成事件,并將每個表寫入專用的事件流。
公開數(shù)據(jù)庫中的基礎(chǔ)表會導(dǎo)致每個表產(chǎn)生相應(yīng)的事件流。雖然這種方式很容易上手,但它會導(dǎo)致多個問題,這些問題可以概括為耦合 問題或成本 問題。讓我們逐一看看。
問題:消費者對內(nèi)部模型的耦合
按原樣公開源Item表會迫使消費者直接耦合它。源系統(tǒng)數(shù)據(jù)模型的更改將影響下游消費者。
假設(shè)我們重構(gòu)Item表以將P ricing提取到它自己的表中。
重構(gòu)源表會導(dǎo)致項目流的數(shù)據(jù)契約損壞。不再向消費者提供他們最初期望的相同商品數(shù)據(jù)。我們還必須創(chuàng)建一個新的連接器 - 一個新的P米流 - 最后,重構(gòu)我們的消費者邏輯 以使其再次運行。重命名列、更改默認值和更改列類型是內(nèi)部數(shù)據(jù)模型上的緊密耦合引入的其他形式的破壞性更改。
問題:流式連接(通常)昂貴
關(guān)系數(shù)據(jù)庫是專門為快速、廉價地解決連接問題而構(gòu)建的。不幸的是,流連接不是這樣的。
考慮兩個需要訪問Item、其Tax及其Brand信息的服務(wù)。如果數(shù)據(jù)已寫入其相應(yīng)的流,則每個使用者(下圖中的右側(cè))將必須計算相同的連接來對Item、B rand和T ax進行非規(guī)范化。
此策略可能會產(chǎn)生高昂的成本,無論是編寫應(yīng)用程序的開發(fā)時間還是計算連接的服務(wù)器成本。大規(guī)模解決流連接可能會導(dǎo)致大量數(shù)據(jù)洗牌,從而產(chǎn)生處理能力、網(wǎng)絡(luò)和存儲成本。此外,并非所有流處理框架都支持連接,尤其是外鍵連接。在那些這樣做的語言中,例如 Flink、Spark、KSQL 或 Kafka Streams(例如),您會發(fā)現(xiàn)自己僅限于編程語言的子集(Java、Scala、Python)。
解決方案:提供非規(guī)范化數(shù)據(jù)是最好的
原則上,應(yīng)使事件流易于消費者使用。在使用抽象層將數(shù)據(jù)提供給消費者之前對其進行非規(guī)范化,并創(chuàng)建顯式的外部模型數(shù)據(jù)契約 (外部數(shù)據(jù))以供消費者耦合。
對內(nèi)部模型的更改在源系統(tǒng)中保持隔離。消費者獲得一個定義明確的數(shù)據(jù)契約來進行耦合。只要源系統(tǒng)為消費者維護數(shù)據(jù)契約,對源模型所做的更改就可以不受阻礙地進行。
但是我們在哪里非規(guī)范化呢?兩種選擇:
· 通過專門構(gòu)建的連接器服務(wù)在源系統(tǒng)外部進行重建。
· 在使用事務(wù)發(fā)件箱模式在源系統(tǒng)中創(chuàng)建事件期間。