支撐支付寶雙11雙12的核心架構(gòu)
本文轉(zhuǎn)載自微信公眾號「安琪拉的博客」,作者安琪拉 。轉(zhuǎn)載本文請聯(lián)系安琪拉的博客公眾號。
現(xiàn)在還依稀記得去年雙11在支付寶作戰(zhàn)室,接近0點的時候,所有人都盯著值班室的秒級監(jiān)控大盤,當交易峰值曲線慢慢爬升,最后變得無比陡峭,值班室的同學都很激動,歡呼聲伴隨著爬升的曲線達到了頂峰,58.3萬筆/秒,也是新的交易峰值記錄,但相比往年動輒翻一倍,漲30%~40%,增長率還是小了很多。
2010年雙11的支付峰值是2萬筆/分鐘,到2017雙11時變?yōu)榱?5.6萬筆/秒,再到去年的58.3萬筆/秒,是09年第一次雙11的一千多倍。
要抗住這么大的支付TPS,螞蟻做了很多頂層架構(gòu)的設計和底層實現(xiàn)的優(yōu)化,其中最為最核心的就是LDC架構(gòu)。
LDC 的全稱為: Logic Data Center, 邏輯數(shù)據(jù)中心,之所以叫LDC,是跟傳統(tǒng)的IDC( Internet Data Center )相比而提出來的概念。
IDC 相信大家都很清楚,就是物理的數(shù)據(jù)中心,說白了就是能夠建站的物理機房。
LDC(邏輯數(shù)據(jù)中心),核心架構(gòu)思想就是不管你物理機房部署是怎樣的,比如你可能有三個IDC,分別在二個不同城市(常說的兩地三中心),在邏輯上是統(tǒng)一的,我邏輯上看成一個整體,統(tǒng)一協(xié)調(diào)調(diào)配。
為什么會出現(xiàn)LDC
LDC是為了解決什么問題?還得從架構(gòu)的演進說起。上一篇文章講的機房容災設計的架構(gòu)演進,我們用具體的應用推演一次。
先看如下圖所示的單體應用架構(gòu),請求到網(wǎng)關(guān)接口,網(wǎng)關(guān)接口直接調(diào)應用或者服務,服務調(diào)存儲層查詢或?qū)懭霐?shù)據(jù),一竿子捅到底。
這種架構(gòu)模式最大的風險是服務、存儲都是單點的,訪問容量和性能受限于存儲和應用的容量和性能,容災方面,一旦發(fā)生故障只能死等單點應用或存儲的恢復。
后來工程師們開始對應用做水平拆分,對服務做垂直拆分。
水平拆分應該都很熟悉,就是加服務器,每臺服務器都部署實例,垂直拆分就是把服務按域做拆分,比如一個交易系統(tǒng),有商戶域、商品域、用戶域、訂單域等,拆分成多個微服務,服務解耦,服務可以獨立發(fā)布,應用的復雜度會更高。
這個分布式架構(gòu)解決了服務單點的問題,某臺服務器宕機,服務還是可用的,但是存儲層還是單點的,而且隨著業(yè)務增長,擴容加的機器越多,大家發(fā)現(xiàn)查詢寫入效率耗時到一定階段反倒是變慢了,分析發(fā)現(xiàn)存儲層出現(xiàn)了性能瓶頸。上面圖只花了2臺服務器連接數(shù)據(jù)庫,真實分布式系統(tǒng)可能幾十百來臺,甚至上千臺,如果都連一臺DB,連接數(shù)、鎖爭用等問題, SQL性能變慢可想而知。
后來的事情大家也都知道,互聯(lián)網(wǎng)公司開始紛紛做讀寫分離,把讀請求和寫請求分開。
讀寫分離這里面隱含了一個邏輯,那就是數(shù)據(jù)寫入之后,不會立即被使用。
數(shù)據(jù)從寫入到被立即使用有個時間差,等從庫同步數(shù)據(jù)才會被讀取,實際統(tǒng)計發(fā)現(xiàn),常規(guī)的應用,90%的數(shù)據(jù)確實在寫入之后不會立即被使用,當然我這里說的立即的時間單位是ms,一般同步延遲也就是幾毫秒,不超過10 ~20ms。
但是這個架構(gòu)并沒有解決寫的問題,隨著業(yè)務量的增長,寫數(shù)據(jù)成為了瓶頸。分庫分表應運而生,分庫分表的中間件開始變得流行起來,現(xiàn)在基本成了中大型互聯(lián)網(wǎng)公司的標配。
基本思想就是把數(shù)據(jù)按照指定維度拆分,比較常見的是userId維度,例如取userId的后2位,可以拆分成百庫百表,也有的除以指定模數(shù)取余數(shù),例如除以64取余,可以按余數(shù)范圍0-63拆成64個庫。
關(guān)于分庫分表,很多人都知道有垂直拆分和水平拆分二種(上面說的垂直和水平是系統(tǒng)的拆分,這里指的是存儲的),垂直拆分就是按照業(yè)務維度拆分,把同一個業(yè)務類型的表放到一個庫,經(jīng)常會按領域模型的概念拆分,比如訂單庫、用戶庫、商品庫等,水平拆分就是把大數(shù)據(jù)量的表(庫)切分成很多個小數(shù)據(jù)量的表(庫),減小庫和表的訪問壓力,可以和系統(tǒng)的水平垂直切分比一下:
水平拆分 | 垂直拆分 | |
---|---|---|
系統(tǒng)維度 | 加服務器 | 大系統(tǒng)按照業(yè)務域拆分成多個子系統(tǒng) |
數(shù)據(jù)庫維度 | 大數(shù)據(jù)量表按照userId分表規(guī)則拆成多個小表 | 大表按照業(yè)務域含義拆分成多個子表 |
為什么叫水平和垂直呢?其實很好理解,你想象一張用戶表,里面放了很多字段,如下圖:
那垂直拆分,就是垂直從中間劃一刀,把藍色的用戶信息表和右邊綠色的訂單信息表拆分成2張表。庫拆分成用戶庫和訂單庫。
水平拆分,就是水平劃一刀,把數(shù)據(jù)量降低。
大家看到這,是不是以為問題都解決了,上面分庫分表之后,如果應用層面扛得住,數(shù)據(jù)庫層面的確能做到并發(fā)量到萬這個級別。但是容量要再上一個數(shù)量級就有點困難了。
為什么呢?因為一個庫實例是被所有應用共享的,也就是你每增加一臺機器,數(shù)據(jù)庫連接就會相應的增加一些,增量是至少機器設置的最小連接數(shù)。
為什么應用需要連接所有的數(shù)據(jù)庫實例呢?
答:網(wǎng)關(guān)層的流量可能走到任何一臺服務器,比如A用戶的請求到服務器上了,這時服務器一定要有A這個用戶userId 分片的數(shù)據(jù)庫連接,否則要么把流量路由走,要么執(zhí)行失敗。
分庫分表只是解決了單庫單表訪問壓力的問題,但是由于每一臺服務器都同時連接所有的分庫實例,到一定階段是沒發(fā)繼續(xù)擴容的,因為庫實例的連接數(shù)有瓶頸。
那數(shù)據(jù)庫存在瓶頸怎么弄?
相信聰明的你們其實已經(jīng)猜到了,那就是按userId 分片在應用層就做隔離,在網(wǎng)關(guān)層流量路由的時候把指定uid分片的流量路由到指定應用單元執(zhí)行,這個應用單元流量內(nèi)部自消化,如下圖:
比如uid = 37487834,最后二位是34 屬于 00-49范圍,那用戶流量直接路由到00-49這個應用分組,在這個單元內(nèi)的完成所有數(shù)據(jù)交互的操作。
這樣uid 00-49 這個分組單元中的應用只用連userId 00-49 分庫的數(shù)據(jù)庫,uid 50-99分組單元的應用也是如此,數(shù)據(jù)庫的連接數(shù)一下直接降一半,而且還可以拆分單元,現(xiàn)在是2個單元,最多可以拆分到100個單元。
這里我加重了單元這個詞,因為這個是LDC中核心概念,下面重點說一下單元這個詞的具體含義。
單元在螞蟻有個名稱叫做 Zone,Zone內(nèi)部署的是完整的服務,例如,一個用戶在一個Zone內(nèi)可以完成一整套業(yè)務流程,流量不需要其他Zone 來提供服務,擁有完成一整套服務的能力,在單個Zone就能完成一整套業(yè)務,是邏輯自包含的,這樣有什么好處,某個Zone如果出現(xiàn)故障,路由層直接把這個Zone流量轉(zhuǎn)移到其他Zone,接受這個Zone流量的其他幾個Zone可以分攤流量,流量調(diào)撥很方便。
下面這張圖是螞蟻Zone 按照地區(qū)和userId 分片的部署架構(gòu)示意圖,做了一些簡化,實際Zone部署單元會稍微復雜一點。
上面介紹的Zone 是有能力完成uid維度的一整套業(yè)務流程的,應用互相依賴的服務都由本Zone提供,服務之間的調(diào)用都在本Zone內(nèi)完成的。但是聰明的你可以會想到一個問題,有的數(shù)據(jù)不能按照userid維度拆分,全局只有一份怎么搞,比如配置中心的數(shù)據(jù),那是集中存儲的,全局只有一份配置,配置后也是全局生效。
其實在螞蟻內(nèi)部,Zone一共分為三種:RZone、GZone、CZone。
RZone: 上面說的邏輯自包含的,業(yè)務系統(tǒng)整體部署的最小單元,能夠按照userId維度拆分服務和庫的都部署在RZone內(nèi)。
GZone:GZone是Global Zone,聽這個名字,也知道,GZone的服務和庫全局只會部署一份,一定是在某個機房的,異地也會部署,但是只是為了災備,不會啟用。
CZone:CZone比較有意思,為什么會有CZone,是為了解決GZone的弊端而產(chǎn)生的,上一篇《從B站崩了看互聯(lián)網(wǎng)公司如何做好高可用》架構(gòu)文章里面講過,跨城調(diào)用,因為距離原因耗時比較高,如果GZone的服務部署在上海,杭州機房的服務需要用到GZone部署的服務,只能跨城跨機房調(diào)用,很可能一個服務有很多次rpc調(diào)用,這樣耗時一定會很爆炸,那怎么弄?在城市與城市之間架起一座數(shù)據(jù)同步的橋梁,CZone就是起到了橋梁的作用,負責把GZone的數(shù)據(jù)在城市之前同步,C是city的意思。
也是因為前面我提到的“寫讀時間差現(xiàn)象”,寫入GZone的數(shù)據(jù),允許一定的延遲,同步CZone同步給其他CZone。