素描單元化
原創聲明:本文系作者原創,謝絕個人、媒體、公眾號或網站未經授權轉載,違者追究其法律責任。
在當今的互聯網業內,不少人對「單元化」這個詞已經耳熟能詳。很多大型互聯網系統,諸如阿里系的淘寶、支付寶、網商銀行等,都已經實現了單元化架構,並從中獲益匪淺。還有更多的公司,或在規劃著自己的系統架構向單元化演進,或已經在單元化的建設過程中。
單元化架構能給系統帶來什麼樣的能力,又會帶來哪些額外的成本,該不該決定做單元化,要做的話應該怎麼做。本文用螞蟻金服支付寶系統的單元化架構建設實踐,為大家勾勒一下單元化的肢體骨架和細枝末節。
為什麼要做單元化
決策一個系統的整體架構方向,將對這個系統的未來產生深遠影響,並且會有實際的技術改造方面的人力投入。這樣的的決策必須是謹慎的,有依據的。所以,對於要不要單元化這個問題,這裡最想告訴大家的是一個忠告:切勿神話單元化。
回顧支付寶的整個單元化歷程,最初促成這個決策的原因是一件看似無關的事情。早在 2011 年,支付寶系統就開始對核心資料庫做水平拆分,而更早之前,對多個關鍵業務資料庫的垂直拆分就已完成。到了 2013 年時,幾乎所有支付寶核心資料庫,都完成了水平拆分,拆分維度為用戶,拆分為 100 個數據分區。此時系統的部署模式是這樣的:
同一個應用的所有節點,都會連接這個業務的所有數據分庫,每個分庫上部署了若干數據分區。任意一個應用節點都可能接收到來自任意用戶的業務請求,然後再根據數據分區規則,訪問對應分庫的數據。
這個架構幫助支付寶系統撐過了 2012 年雙 11,卻無論如何過不了 2013 年大促了,原因在於資料庫連接不夠用了。主流的商業資料庫,連接都不是共享的,就是說一個事務必須獨佔一個連接。而連接卻又是資料庫非常寶貴的資源,不能無限增加。當時的支付寶,面臨的問題是不能再對應用集群擴容,因為每加一台機器,就需要在每個數據分庫上新增若干連接,而此時幾個核心資料庫的連接數已經到達上限。應用不能擴容,意味著支付寶系統的容量定格了,不能再有任何業務量增長,別說大促,很可能再過一段時間連日常業務也支撐不了了。 如果 OceanBase 在當時已經成熟,可以很好的解決 Sharding 帶來的連接數瓶頸問題,因為 OceanBase 是分散式資料庫,連接可以共享。然而那時並沒有一個合適的經過驗證的共享連接資料庫產品,因此單元化成為了最好的也是唯一的解決辦法。
根據單元化的特性,每個單元中的應用伺服器僅連接本單元的數據分庫,如果有 n 個單元,這就相當於所有數據分庫的連接數立刻降為原來的 1/n。
從支付寶並沒有倒在 2013 年雙 11 可以知道,這次架構改造是成功的,解決了當初的燃眉之急,並且在落地單元化架構的過程中,螞蟻技術團隊也充分拓展了單元化架構的應用場景和架構的能力,從中獲得了更多的架構紅利,主要表現在以下幾個方面:
- 無限可伸縮微服務架構:通過中間件和 PaaS 平台的配合,能夠通過快速搭建一個業務完整的邏輯部署單元對系統進行整體擴容,對新機房擴容等操作帶來了非常大的便利。突破接入層、應用層和數據層的瓶頸,可支持無限擴展。
- 異地多活部署:除了具備異地容災能力以外,還能做到異地多城市多活,可隨時在多個城市間調配流量比例,在提升容災能力的同時,降低了成本。
- 全站藍綠髮布和線上灰度模擬:通過多單元之間靈活的流量調配機制,可以實現大規模集群的藍綠髮布,極大的提升了發布效率。同時,通過單元內自包含的服務發現/路由和數據層的單元化分片,保證故障被切割的更小且具備獨立性,不會傳播到其他機房,從而實現發布時的故障自包含,螞蟻基於這個機制實現了線上全鏈路壓測和灰度模擬環境,為業務提供了更真實的驗證環境,這對充分驗證業務正確性,降低技術故障起到了關鍵的作用,尤其對金融類業務。
- 異構機房上的彈性混合雲架構:通過單元化伸縮的機制和容器化技術對底層虛擬化平台的屏蔽,實現多個異構機房的資源充分利用,無論基於什麼架構,無論在哪個城市,都可以快速建站部署單元,並在業務高峰期過後快速回收,完成數據的回遷。
雖然結果很好,整個單元化演進的過程也是極其艱辛的。在關鍵業務資料庫都已經水平拆分完畢的情況下,支付寶還是用了將近 1 年的時間,改造了幾乎全站業務系統以支持單元化。除此之外,還花費了相當大的力量改造系統使用的各類中間件,包括微服務框架、消息中間件、調度系統等,還建設了專門的單元化運維監控平台。無論在架構方案上還是技術產品上,螞蟻技術團隊都積累了大量的經驗。
這個過程帶來的啟示是,單元化確實能帶來不少激動人心的特性,但同時也會讓系統架構變的複雜。這不僅會對系統的運維平台提出更高要求,也會給業務研髮帶來一些理解成本,這個思想非常像業界流行的 Cloud Native 的思想,要充分利用雲架構的好處,還需要讓應用層架構設計遵照一定的範式,而螞蟻逐步將這些範式和能力沉澱到了 PaaS 平台、中間件和資料庫中,成為一種原生 (Native) 的能力,為業務開發提供單元化能力支撐。
所以說,單元化不能被神話,它有利有弊,只是看著它的好處就貿然決定開始的做法不可取。應當充分評估當前的系統現狀,識別其中存在的和潛在的問題,判斷單元化是否能解決以及解決所帶來的成本,選擇最合適的性價比最高的架構來支撐業務發展。 當然,如果最後的判斷是需要引入單元化,那麼一定義無反顧,畢竟它產生的價值及其誘人。
能不能單元化
如果您是新建一套系統,那麼恭喜你,你只要考慮這個系統的重要性,即將來是否需要在可擴展性、容災能力、業務連續性、成本控制等方面有很高的要求,如果答案是肯定的,那可以一開始就按照單元化的方式去設計,這套架構完全能支持業務從單機房到同城多機房,再到異地多活機房的平滑演進。但如果是一套老系統,就需要評估一下它是否具備單元化的基礎了。
所謂單元,是指一個能完成所有業務操作的自包含集合,在這個集合中包含了所有業務所需的所有服務,以及分配給這個單元的數據。單元化架構就是把單元作為系統部署的基本單位,在全站所有機房中部署數個單元,每個機房裡的單元數目不定,任意一個單元都部署了系統所需的所有的應用,數據則是全量數據按照某種維度劃分後的一部分。 傳統意義上的 SOA 化(服務化)架構,服務是分層的,每層的節點數量不盡相同,上層調用下層時,隨機選擇節點。
單元化架構下,服務仍然是分層的,不同的是每一層中的任意一個節點都屬於且僅屬於某一個單元,上層調用下層時,僅會選擇本單元內的節點。
一個單元,是一個五臟俱全的縮小版整站,它是全能的,因為部署了所有應用;但它不是全量的,因為只能操作一部分數據。能夠單元化的系統,很容易在多機房中部署,因為可以輕易的把幾個單元部署在一個機房,而把另外幾個部署在其他機房。藉由在業務入口處設置一個流量調配器,可以調整業務流量在單元之間的比例。
從這個對單元的定義和特性描述中,可以推導出單元化架構要求系統必須具備的一項能力:數據分區,實際上正是數據分區決定了各個單元可承擔的業務流量比例。數據分區(shard),即是把全局數據按照某一個維度水平劃分開來,每個分區的數據內容互不重疊,這也就是資料庫水平拆分所做的事情。 僅把數據分區了還不夠,單元化的另外一個必要條件是,全站所有業務數據分區所用的拆分維度和拆分規則都必須一樣。若是以用戶分區數據,那交易、收單、微貸、支付、賬務等,全鏈路業務都應該基於用戶維度拆分數據,並且採用一樣的規則拆分出同樣的分區數。比如,以用戶 id 末 2 位作為標識,將每個業務的全量數據都劃分為 100 個分區(00-99)。
有了以上兩個基礎,單元化才可能成為現實。把一個或幾個數據分區,部署在某個單元里,這些數據分區佔總量數據的比例,就是這個單元能夠承擔的業務流量比例。 選擇數據分區維度,是個很重要的問題。一個好的維度,應該: 粒度合適。粒度過大,會讓流量調配的靈活性和精細度收到制約;粒度過小,會給數據的支撐資源、訪問邏輯帶來負擔。 足夠平均。按這個維度劃分後,每個分區的數據量應該幾乎一致。 以用戶為服務主體的系統(To C),比如支付寶,通常可以按照用戶維度對數據分區,這是一個最佳實踐。
怎麼做單元化
在真正動手之前,有一個必須要知曉並始終記之在心的事實,完美的單元化是不存在的。 從單元化的角度,在一個系統當中實際上存在 3 類數據:
1、可分區數據
可以按照選擇好的維度進行分區的數據,真正能被單元化的數據。這類數據通常在系統業務鏈路中處於核心位置,單元化建設最重要的目標實際上就是把這些數據處理好。比如訂單數據、支付流水數據、賬戶數據等,都屬於這一類型。 這類數據在系統中的佔比越高,整體單元化的程度就越高,如果系統中全部都是這樣的數據,那我們就能打造一個完美單元化的架構。不過現實中這種情況存在的可能性幾乎為零,因為下面提到的兩類數據,或多或少都會存在於系統當中。
2、全局數據,不被關鍵鏈路業務頻繁訪問
不能被分區的數據,全局只能有一份。比較典型的是一些配置類數據,它們可能會被關鍵鏈路業務訪問,但並不頻繁,因此即使訪問速度不夠快,也不會對業務性能造成太大的影響。 因為不能分區,這類數據不能被部署在經典的單元中,必須創造一種非典型單元用以承載它們。
3、全局數據,需要被關鍵鏈路業務頻繁訪問
乍看與上面一類相似,但兩者有一個顯著的區別,即是否會被關鍵鏈路業務頻繁訪問。如果系統不追求異地部署,那麼這個區別不會產生什麼影響;但如果希望通過單元化獲得多地多活的能力,這僅有的一點兒不同,會讓對這兩類數據的處理方式截然不同,後者所要消耗的成本和帶來的複雜度都大幅增加。
究其原因是異地部署所產生的網路時延問題。根據實際測試,在網路施工精細的前提下,相距約 2000 公里的 2 個機房,單向通信延時大約 20ms 左右,據此推算在國內任意兩地部署的機房,之間延時在 30ms 上下。假如一筆業務需要 1 次異地機房的同步調用,就需要至少 60ms 的延時(請求去,響應回)。如果某個不能單元化的數據需要被關鍵業務頻繁訪問,而業務的大部分服務都部署在異地單元中,網路耗時 60ms 的調用在一筆業務中可能有個幾十次,這就是說有可能用戶點擊一個按鈕後,要等待數秒甚至數十秒,系統的服務性能被大幅拉低。
這類數據的典型代表是會員數據,對於支付寶這類 To C 的系統來說,幾乎所有的業務都需要使用到會員信息,而會員數據卻又是公共的。因為業務必然是雙邊的,會員數據是不能以用戶維度分區的。
支付寶的單元化架構中,把單元稱之為 「zone」,並且為上面所說的3類數據分別設計了三種不同類型的 zone:
- RZone(Region Zone):最符合理論上單元定義的 zone,每個 RZone 都是自包含的,擁有自己的數據,能完成所有業務。
- GZone(Global Zone):部署了不可拆分的數據和服務,這些數據或服務可能會被RZone依賴。GZone 在全局只有一組,數據僅有一份。
- CZone(City Zone):同樣部署了不可拆分的數據和服務,也會被 RZone 依賴。跟 GZone 不同的是,CZone 中的數據或服務會被 RZone 頻繁訪問,每一筆業務至少會訪問一次;而 GZone 被 RZone 訪問的頻率則低的多。
RZone 是成組部署的,組內 A/B 集群互為備份,可隨時調整 A/B 之間的流量比例。可以把一組 RZone 部署的任意機房中,包括異地機房,數據隨著 zone 一起走。
GZone 也是成組部署的,A/B 互備,同樣可以調整流量。GZone 只有一組,必須部署在同一個城市中。
CZone 是一種很特殊的 zone,它是為了解決最讓人頭疼的異地延時問題而誕生的,可以說是支付寶單元化架構的一個創新。 CZone 解決這個問題的核心思想是:把數據搬到本地,並基於一個假設:大部分數據被創建(寫入)和被使用(讀取)之間是有時間差的。
- 把數據搬到本地:在某個機房創建或更新的公共數據,以增量的方式同步給異地所有機房,並且同步是雙向的,也就是說在大多數時間,所有機房裡的公共資料庫,內容都是一樣的。這就使得部署在任何城市的 RZone,都可以在本地訪問公共數據,消除了跨地訪問的影響。整個過程中唯一受到異地延時影響的,就只有數據同步,而這影響,也會被下面所說的時間差抹掉。
- 時間差假設:舉例說明,2 個用戶分屬兩個不同的 RZone,分別部署在兩地,用戶 A 要給用戶 B 做一筆轉賬,系統處理時必須同時拿到 A 和 B 的會員信息;而 B 是一個剛剛新建的用戶,它創建後,其會員信息會進入它所在機房的公共資料庫,然後再同步給 A 所在的機房。如果 A 發起轉賬的時候,B 的信息還沒有同步給 A 的機房,這筆業務就會失敗。時間差假設就是,對於 80% 以上的公共數據,這種情況不會發生,也就是說 B 的會員信息創建後,過了足夠長的時間後,A 才會發起對 B 的轉賬。
通過對支付寶 RZone 業務的分析發現,時間差假設是成立的,實際上超過 90% 的業務,都對數據被創建和被使用之間的時間間隔要求很低。餘下的那些不能忍受時間差的業務(即要求數據被創建後就必須馬上可用,要不就乾脆不能訪問),則必須進行業務改造,忍受異地訪問延時。
需要哪些支持
前面提到,支付寶系統早已實現了數據水平拆分,並且全站拆分維度一致,在具備了這個重要基礎能力的前提下,仍然舉全站之力用了近一年時間才完成單元化改造,除了因為要改造業務系統以妥善處理 GZone/CZone 的數據以外,還有一項工作也耗費了大量人力和時間,那就是建設起了一個單元化技術支持平台。 單元化架構增加了構複雜性和運維複雜度,如果沒有這樣一個平台支撐,很難把如此複雜的一個系統健康的維護起來。 所謂單元化技術支持平台,至少應該包含三方面功能。
一套支持單元化的中間件
支付寶的業務開發因單元化架構而變得複雜、不易理解,而實際上現在看到的複雜性比起它的原本的程度來說已經降低了太多。這其中最大程度屏蔽了單元化細節,讓單元化架構盡量對業務層透明,起到至關重要作用的,就是一整套專門針對單元化而研製的中間件,也即上文提到的原生 (Native) 單元化能力。
比較關鍵的幾個包括,數據訪問層中間件,把數據水平分庫分表的細節隱藏在水面之下,上層業務使用起來跟單庫單表沒什麼區別。更進一步的,數據訪問層能實時動態感知單元化分流規則,根據規則變化決策是否連接某個數據分庫,以及一筆業務是否允許訪問某個數據分庫。可以說單元化的本質就是對數據的邏輯隔離,有這樣一個數據訪問中間件對單元化建設來說是必不可少的,它解耦了業務層對數據層結構的依賴,大大降低了理解成本。
另外更為重要的是幾個負責信息傳遞的中間件,正是它們讓單元可以成為單元。這其中有微服務框架、消息中間件等,它們能夠根據單元化規則把系統應用間的服務調用或消息流轉約束在一個邏輯區域內,再配合上數據訪問中間件對數據訪問的約束,就讓這一個個邏輯區域形成了一個個單元。如果有些調用必須跨單元發生,也由它們根據規則執行轉發,不需要業務層關心。
一個規則管理和流量調撥系統
前面多次提到了「規則」這個詞,在單元化架構中,規則舉足輕重,業務流量在單元間怎麼分配,哪個單元能訪問哪個數據分庫,某次服務調用能否跨出單元,這些都由規則掌管。可想而知,必須有一個統一的位置來全局唯一的管理這套規則,並在變更時能夠及時準確的的推送給各個執行點,否則不同模塊識別到的規則不同,將會造成全系統信息混亂。
規則管理系統的目的就是成為這樣一個單元化規則權威持有者,運維人員對規則的所有變更,都在這個系統上操作。它通過某種協議連接著系統中所有需要感知規則的模塊,一旦發生規則變化就實時把新規則內容推送給各個模塊,並識別推送結果,對推送失敗的模塊執行重試,確保規則下發到所有位置。
一個面向單元的發布部署和監控平台
單元化後,一個系統被劃分成了數量眾多的邏輯單元,這讓系統發布換版的粒度更細,試錯成本更低,但也讓發布這件事情更難做,所以一個能面向單元進行發布的部署平台就變的非常必要。 這個部署平台能夠以單元為維度執行發布,單元間可以互不影響的獨立部署。
同時,平台需要有能力統一編排和控制不同單元的部署進程,從而能夠實現基於單元的全站藍綠髮布等高階功能。
此外,監控系統要求能夠細化到單元粒度展示監控信息和發送報警,以協助判斷每個單元中的系統健康程度,為快速執行流量切換以隔離故障等操作提供依據。
以上幾項是一個單元化技術支持平台必須要具備的能力,在支付寶的單元化進程中,已經沉澱出了完整的這樣一套平台,並在逐步產品化為具備完整單元化能力的 SOFA 平台和 OceanBase 資料庫,搭載在螞蟻金融雲上對外輸出。在幾年中先後支持了螞蟻保險、網商銀行的單元化改造,在未來也將為更多的金融生態合作夥伴提供服務。
參考
金融電子化雜誌 2016年12期 《金融級分散式交易的技術路徑》 程立
公眾號:金融級分散式架構(Antfin_SOFA)
推薦閱讀: