唯品會實時計算平臺的演進之路

本文由 dbaplus 社羣授權轉載。

一、實時平臺現狀

先介紹一下我們整個平臺的現狀,按計算的話,分爲Storm、Spark、Flink三個主要的計算引擎,Flink相應的應用數量目前少一些,不過按照整個計算引擎的發展方式,後續我們還是希望以Flink爲主做相應的業務推進。

1、核心業務

  • 實時推薦引擎:這塊是非常核心的業務,對於大數據來說這些都是個性化推薦、實時推薦;
  • 實時看板:我們稱爲給到總裁級別、各個業務線的運營和商務的數據,例如各個業務線上的實時銷售和用戶訪問數據;
    實時數據,包括MySQL binlog數據和用戶行爲數據:

我們通過VDP將binlog解析後同步到Kafka中,給到後續業務方;

  • 用戶行爲數據是把app或者微信等埋點曝光的數據清洗出來,給後續的實時應用;
  • 金融風控與安全業務的風控;
  • 比價:大家在購買電商商品的時候會比價,我們平臺方面也會做,看同樣一件商品在其他電商售價多少,以決定我們自己的價格策略;
  • 監控:我們有很多實時的監控系統業務指標,需要利用到實時的指標,比如全站的PV、UV等。

2、實時平臺的職責

整個實時平臺我們可以看兩大板塊:

  • 實時計算平臺:提供基礎數據的支持,保證應用的監控數據的穩定性,提供相應的開發支持,包括應用的開發方案評審和引導;
  • 實時基礎數據:很多時候我們需要提供基礎數據(埋點、Binlog數據)的清洗打寬,這方面技術性也很強,會彙集到各種業務方。此外,格式的定義、自然的監控也是我們來做,會提供技術平臺和技術數據,這些體現着平臺的重要職責。

二、實時平臺的發展歷程

1、早期

整個實時平臺的發展過程中,實時這方面也是從0到1,大規模開始進行實時的業務大概是在2013、2014年,期間在不斷地產生和迭代。

  • Storm是最早成熟的實時計算框架,最開始也是基於Storm做實時應用開發和推廣。
  • 早期也在Storm嘗試去做SQL相關的事,那時候想法挺美好,但是因爲實時的SQL能完成的事情,包括框架的成熟度比較低的情況下,SQL相互碰撞,步子邁得太大了一點,就死在了沙灘上,我們當時能夠真正幫到業務用SQL完成的事情還太少。現在相對而言,實時框架上的SQL(Flink SQL)完善了很多,可以推廣使用。
  • 有段時間也做所有實時應用的對接,對我們來說最大的就是資源瓶頸,人員不足,後面還是聚焦平臺爲主,包括負責部分核心應用。

2、現在

整個實時平臺的發展過程經歷了幾個階段,具體的技術發展過程如下:

首先是有了VRC,用於任務管理、監控、告警、數據質量。實時應用都是線上的應用,沒有平臺統一管控的話,實時應用是否OK或數據是否延遲等情況出現,就都不知道。有了實時平臺之後,我們就知道實際應用運行到底怎麼樣。

Storm比較完善後,計算框架增加Spark Streaming。

很多用戶會用到我們提供基礎的數據,比如流量的數據、Binlog等,並且我們會提供業務上的聚合和打寬。因爲很多情況下我們用流量數據時,希望這個數據能包含的維度比較多,最典型的維度是用戶過來以後,基本上有對應的設備ID轉換成唯品會的ID。

這個設備ID關聯的用戶的性別、姓名,或者用戶的基本會員等級、用戶偏好等一些基礎的數據我們直接關聯好,後續做實時任務就簡單了很多。

2018年我們開始推廣Flink相關的應用,團隊內部做的應用全部切到了Flink。也是從2018年6月開始,調度開始切換到Kubernetes上。我們的實時平臺不僅做實時相關的事,也做很多跟機器學習相關的工作,希望機器資源能更好地去共享。

機器資源對於電商來說,會有一個很明顯的特徵。在618、雙十一這樣的大促時,隨着數據量的暴增,資源的消耗量也會迅猛增長。

大促結束之後,這些應用根本就不需要這些資源,那麼在平常一些時候,這些資源可以給機器學習更多的資源,通過統一資源管理框架調度系統做的這件事情,這是我們切換到Kubernetes上的很重要的原因。

我們一直往後面推進,裏面也有很多的技術問題需要解決。

3、平臺架構

實時平臺來說,各家公司的架構基本上差不多。主要的核心數據就是兩方面:一是數據源自用戶行爲的數據;另外就是線上對接的業務數據庫的數據。

實時數據到達後,通過計算引擎完成業務邏輯的計算,以及和其他數據的交互,包括支持實時訪問的存儲系統。數據的結果可以給到實時報表、推薦引擎,也有給到機器學習平臺進行模型訓練。

整個數據流的過程會經歷很多的環節,通過很多次計算的過程,對穩定性和可靠性都有比較高的要求,核心體系架構如下所示:

實時平臺提供一個交互式開發的環境,一些簡單的邏輯或者可以用SQL表達到的;複雜邏輯會使用計算框架的API進行開發。

對於任務來說,版本、數據質量以及元數據系統和離線還不太一樣。離線的元數據系統都是表,任務很多都是依賴報表來完成。

實時的元數據系統就會考慮到它是在哪;我怎麼可以訪問到它;它裏面數據的格式是怎樣的。因爲數據可能是PB格式,也可能是JSON,我們會考慮怎麼把這個數據格式對業務方透明,在上面做相應的封裝來保證業務的方式比較簡潔。

實時計算在從早期的荒野到現在逐步完善,我用痛並快樂來形容:痛是因爲它開發的難度、開發的要求比離線和其他應用開發要難;快樂在問題解決以後,以及對業務的增長帶來成就。

三、實時計算的難點和挑戰

1、開發複雜度

首先說一下實時開發的挑戰。比如實時所有的數據,在我們的處理中都是以流形式過來的,沒有開始和結束的概念,離線的如果用Spark SQL去處理數據就有明確的開始結束時間。

就如我們做訂單處理,其實這個訂單的狀態是一直變化的。如添加的購物車、下單、支付、物流和收貨以及售後等。

物流之前,訂單還會扭轉到物流倉庫。在包括售後和退貨等環節後,這整個訂單的持續過程會很久,它隨時隨地在變化。因爲一個月以後訂單發生的質量問題,也需要平臺來處理。

整個計算鏈路也比較長,同時涉及到的系統有很多,對計算引擎、存儲這些都會要求。

相應地,做實時開發有幾個討論的核心問題:

  • 設置各個組件的並行度是不是合理?是不是可以滿足需求?怎麼去評估我這是合理的還是說它太大還是太小?
  • 面對計算的結果和內部的計算中間結果數據,用什麼來存儲?中間是沒有狀態數據的,這狀態數據如果量比較大,並且需要跟外部系統密切交互的時候,如果外部系統響應比較慢或者延遲比較高,就會嚴重影響實時處理的效率;
  • 異常處理困難,多個流之間的處理很具有挑戰性,現在來說,沒有一個非常完美的方案能解決多個流之間的關聯關係。離線處理相對比較簡單,但是在實時處理的時候,問題就複雜了很多;
  • 一致性問題:實時數據要做到Exactly Once也是非常有挑戰的,對系統有非常高的要求;
  • 數據準確性和校驗,很多時候做的實時指標都要跟離線進行對數,如何保證實時和離線一致的,需要考慮到的問題就特別多。

2、技術難度

亂序問題

多個流要關聯的話,亂序問題是非常麻煩的。因爲很多的數據過來的時候會存在亂序問題。有一條數據可能是早發生的,但是後收到了,因爲整個數據上報過來的時候,經歷了很多很多的重輸,經歷了各種設備,很難保證數據是嚴格有序的。

吞吐和延遲

延遲和吞吐TPS通常是矛盾的,你希望TPS高,怎麼辦?

通常加大批處理的模式,我希望一次可以處理一批數據,這樣吞吐量就上去了;但是這樣延遲就會增加。很多時候面臨的具體是什麼問題,根據具體問題來給出解決方案。

內部的狀態數量

比如計算UV,UV的維度會非常多,甚至細到商品粒度。

我們在線的商品幾百萬的時候,對於商品的UV,以及跟商品關聯的的品牌、檔期等,這些交叉之後所需要的計算的指標特別大,可能達到千萬級的統計量。

怎麼去做狀態數據的存儲,包括這些數據的可用性,就非常重要了。如果任務處理失敗了,怎麼快速的恢復等。

四、實時平臺的發展方向

對於實時平臺來說,這些年的發展都是在做一件事情——降低開發門檻。首先,從平臺本身來說,我們做了這幾件事情:

1、豐富實時基礎數據——實時離線融合

提供核心基礎數據

我們做的第一件事是提供核心基礎數據。這個很多時候可以降低實時業務後續開發難度。因爲前面我們剛剛提到了做多種實時寬表以後,業務做實時應用其實就只是處理單一流。

在前置的任務中,我們把很多特別難搞的問題都解決掉,包括關聯到其他維度的數據,多個數據源之間的亂序等,關聯好的數據輸出到一個流中,後續實時的應用處理單一流就簡單了很多。

實時和離線統一

因爲大家都知道有一種Labmda提法,我實時地算一遍,然後用批處理去補數。

但這會面臨兩個問題,一是數據需要兩個計算框架,工作量大了很多;二是數據重複計算,導致更多的資源消耗。

我們現在來做裏面的事情,能實時處理的數據就不會再去離線處理。因爲實時處理的實時清洗打寬以後,我們以五分鐘的粒度寫入HDFS的準實時表裏面,離線任務就可以啓動來使用這些數據了。這樣,很多數據的的口徑就做到了一致性。

業務方有需求要一個他們認爲的實時數據指標,首先作爲平臺來說,就會問你一個直白的問題:你希望這個實時是什麼概念?是秒級還是分鐘級?

因爲對於實時的時間密度大家理解是不一致的,如果在分鐘級以上我們會根據這些數據通過準實時或者離線的方案來完成數據的輸出。因爲離線SQL的開發成本是最低的,可能我直接寫SQL就能完成。

相應的,如果你真的要求數據時間非常短,那要考慮真正實時的應用怎麼開發。

2、統一計算資源:基於Kubernetes調度實時和AI平臺資源

前面我們也提到了在管理計算資源的時候,如果做到Storm、Flink或者Spark等任務通過Kubernetes統一資源的調度管理後,能快速地對應用擴收容。實時平臺的管理實時應用接近800個,所以需要快速地提升自身應用的能力和變更。

3、新的開發模式

實時平臺現在推廣基於Streaming SQL的開發模式,底層的計算引擎以Flink和Spark Structured Streaming爲主。

因爲不管是Flink還是Spark Structured Streaming,都支持狀態存儲,便於錯誤恢復和可靠性保障。

第二個是相應的,我們整個API要更高級一些。相應的,寫一些代碼的時候,也會有非常大的簡化。

整個來說,開發平臺也在預研實時的notebook開發環境,你可以完成相應的實時應用的開發,整個模式都是在替換Storm的節奏。

4、效率提升

在我們的實踐中,舉一些使用Flink帶來的顯著好處的案例。

UV計算

在以Storm爲計算框架的時候,狀態存儲通常是在Redis中,這樣會需要大量的Redis資源。

現在完全可以用Flink來做,它的好處是我們有很多的計算任務,通過這樣的轉化,它計算的穩定性、可靠性得以提升,計算資源消耗降低2/3。

以我們自己UV中品牌日這個場景爲例,同時會有上百個品牌日在線,因此每個品牌日的指標需要全部的預算指標。在Storm爲計算的時候甚至用到了240個以上的Worker節點,並且配合了2T的Redis存儲。

切到了Flink以後,因爲Flink所有的狀態都是在內存中,這個過程中少了跟Redis的交互,效率會有非常大的提升。同時整個的依賴組件越少,系統穩定性就會越高。

埋點數據落地

我們很多實時的在Kafka裏面的數據通過Spark Streaming寫入到HDFS中,然後通過Hive、Spark SQL來訪問。

在使用Flink裏帶的Bucket Sink模式,只需要十分之一的資源就達到了原來Spark Streaming就能達到相應的吞吐量,並且延遲也大幅度的降低。

5、統一數據源

1)UDM架構

當前實時系統,機器學習平臺要處理的數據分佈在各種數據存儲組件中,如Kafka、Redis、Tair、HDFS等等,如何方便高效的訪問、處理、共享這些數據是一個很大的挑戰,對於當前的數據訪問和解析常常需要耗費很多的精力。主要的痛點包括:

  • 對於Kafka,Redis,Tair中的binary(PB/Avro等格式)數據,使用者無法快速直接的瞭解數據的schema與數據內容,採集數據內容及與寫入者的溝通成本很高。
  • 由於缺少獨立的統一數據系統服務,對Kafka,Redis,Tair等中的binary數據訪問需要依賴寫入者提供的信息,如proto生成類,數據格式wiki定義等,維護成本高,容易出錯。
  • 缺乏relational schema使得使用者無法直接基於更高效易用的SQL或LINQ層API開發業務。
  • 無法通過一個獨立的服務方便的發佈和共享數據。
  • 實時數據無法直接提供給Batch SQL引擎使用。
  • 此外,對於當前大部分的數據源的訪問也缺少審計,權限管理,訪問監控,跟蹤等特性。

UDM(統一數據管理系統)包括Location Manager、 Schema Metastore以及Client Proxy等模塊,主要的功能包括:

  • 提供從名字到地址的映射服務,使用者通過抽象名字而不是具體地址訪問數據。
  • 用戶可以方便的通過Web GUI界面方便的查看數據Schema,探查數據內容。
  • 提供支持審計,監控,溯源等附加功能的Client API Proxy。
  • 在Spark/Flink/Storm等框架中,以最適合使用的形式提供這些數據源的封裝。

UDM的體系架構如下圖所示:

2)基於UDM的開發模式

在基於UDM的基礎上,實時和離線可以統一開發模式,其示例代碼如下:

從示例可以看出,不管Flink還是Spark都是統一的開發模式,而且代碼非常簡潔。

Q & A

Q1:我們也是做這個的,你的大數據平臺上所有的組件都是容器化的還是混合的?是屬於計算類的?

A1:現在大數據的所有組件要容器化很難,存儲我們還沒有做容器化相關的事,更多的是做偏計算引擎,就是我計算容器化掉,能給到很多更明確的意義。

比如說我很難告訴你一個在YARN上的任務到底吃掉了多少CPU,到底系統有沒有問題。那現在容器化後就可以非常好地監控應用的資源消耗,也非常容易判斷資源是否使用合理。

第二個,容器化以後,可以開放一些更高的權限給應用的開發者,我可以允許你進到容器裏去做一些以前需要高權限才能做的事,比如說要去做一些性能分析問題的時候,在物理機上不太可能給你比較高的權限的,你進去看到的包括其他開發者的應用,操作不當可能把組件都搞掛了。

但是在容器環境下,你登錄到容器裏面,搞死了就是你自己把自己的應用搞死了,別人的你根本就看不到。

我們目前是計算類的。對於大數據無外乎就計算和存儲兩大類。存儲容器化目前來說,優勢不明顯,反而會帶來更多問題。

現在對於大數據來說,一個趨勢是以前我們希望計算和存儲在一起,現在由於網絡帶寬越來越高,我們偏好計算和存儲分離。因爲大的互聯網公司基本上都是從至少單機萬兆以上的網絡。相對而言,數據的本地性重要性就沒有那麼高。存儲和計算分離以後,計算節點擴展就更加容易。

Q2:我想問下,如果在流計算的統計過程中要維護很多狀態,比如有幾十億的要維護,根據您剛剛提到的技術難點,能講講是怎樣解決的嗎?下一個問題是各個節點寫入不均衡,怎麼去監控,怎麼解決?怎麼樣看數據傾斜呢?

A2:第一個問題,如果以Storm來說,可以做兩級存儲,依賴Storm我肯定會在自己Worker的JVM內部做一級緩存。

達到一定的時間和一定量之後,刷到外部的二級存儲裏面。計算維度數據的統計指標非常龐大的時候,如果直接往外部輸,說得直白一點,再強悍的外部存儲的性能都扛不住。你也提到了上億的時候,每秒輸出的數據都是幾百萬QPS,基本上沒有一個存儲的引擎可以在比較合理的規模來支持這個量級(除非依賴更大規模的分佈式存儲引擎)。

因此一般都是兩級存儲,到了一定時間或者一定狀態數量之後把它刷新到外部的存儲引擎。這樣就有一個問題,你會丟掉最近未刷新出去的那批數據,這就要求你可以從上一個刷新出去的數據狀態支持恢復,或者重複處理。

Storm完全依賴自己的狀態管理,數據出現失敗,或者恢復的時候,需要自己處理比較多的事情。Flink相對而言就好很多,支持從checkpoint或者savepoint直接恢復。

第二個問題對於數據傾斜來說,基本上所有的數據傾斜的處理都是兩階段處理。即先做一級計算彙總,一級計算之後再做二級處理;或者明確知道哪些數據分佈特別不均勻,把這些數據單獨處理。

關於怎麼樣看數據傾斜,觀察每一級Task往下一級Task寫出的數據量的分佈就可以了。在我看來,數據量最大的比最小的高了十倍就肯定是傾斜,當然也可以計算標準差。

Q3:您之前說唯品會是電商方面的,我們公司也是電商方面的。想問關於訂單方面的狀態您是怎麼維持它的瞬時性的?

A3:對於訂單,或者還是其他的業務數據,就是兩種場景,一種場景要求我們自己做的工具,我們對應的同一張表,哪怕是分表的數據,同一個表或者分庫分表寫到同一個Kafka的Topic裏面,並且根據分表做Partition映射,這樣可以保證局部一致性,即同一個物理表的數據是嚴格有序的。要保證全局一致性,其實是保證不了的。

但是,對業務來說,局部的一致性就OK了,同一個訂單隻能寫到同一個分表上。這樣在讀取數據的時候,就能做到按相應順序消費。

第二種,像一些特殊的場景,可能還是會依賴我們自己把秒級數據抽取,即高頻的以SQL方式去拉取最新的數據寫入到Kafka中。

很多時候我們做訂單統計,也會緩存所有的訂單計算過程一些中間狀態。最典型的,訂單的中間狀態的變化是很複雜的一件事。我下單時第一件事是拆單,一個訂單能買三件商品,我們有複雜的拆單邏輯,即將這個母單會拆成三個子單,或者兩個子單。

因爲對應了不同的物流倉庫,也可能有下的海淘的商品。在簽收之前的過程中你可能又取消了一個子單,或者把三個子單都取消,那這個訂單就徹底取消掉了。

因此我們算單數的時候,就要全部減掉。整訂單的各種狀態我們會把它緩存起來。因爲像我們的訂單大概有接近40個狀態,只是有些狀態可能不見得會在統計的時候用到。

##作者介紹

王新春,唯品會高級經理、數據平臺實時團隊高級架構師,主要負責實時計算平臺、實時數據、實時報表和機器學習平臺等業務;曾任美團點評(原大衆點評)數據平臺高級架構師,負責從零開始搭建實時計算平臺及數據平臺工具體系開發和建設等工作。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章