1号店交易系统架构如何向「高并发高可用」演进
http://blog.csdn.net/lz0426001/article/details/51441893
輕量級電商的架構和痛點
大家看上圖,一個輕量級的電商網站應用架構就是這樣的,比如說你現在想做一個電商網站,你是創業公司,兩三個人開始做,估計架構就是這樣的。前端有PC、App和H5,有表現層、業務邏輯層和數據訪問層等。
重量級的電商網站應用架構是怎么重的呢?很簡單,隨著業務的擴展,業務量多了、代碼量多了、數據量大了、并發量高了。1號店是一家電商網站,但嚴格來講并不是百分之百的互聯網公司,我們更多是業務驅動型而不是技術驅動型公司。因為技術是為業務服務的,反過來說,技術也可以驅動業務,如果我們的技術能力支持不到1號店的業務體量的增長,支持不了那么高的并發量的話,網站很容易就掛了。
一些輕量級電商網站的架構痛點是什么?
首先說它的特點,業務高速發展,業務形式多樣,人員規模爆增。這里所說的人員更多是技術人員,一開始是兩三個人,可能是面對面的,后來變成幾十人、幾百人甚至是幾千人上萬人的規模。當然它的請求量、并發量、數據存儲量都非常的高。
痛點也很多,首先是代碼耦合,一開始可能就一個人就全部搞定了。因為一開始就一、兩個人,不需要那么多。但從業務端的增長來看,一、兩個人進行業務支持就有問題了,業務響應慢,業務互相影響。還有在出現問題時定位很難,有的時候還會責任不清,遇到一些互相扯皮的情況,表現出來的就是系統不穩定。
表結構也非常混亂,今天來一個業務加一個,明天來個業務又加一個。數據庫單點,也是我們非常大的痛點,一旦數據庫掛了系統就徹底崩潰了。
還有監控預警的問題,這是一個非常大的痛點。我們知道類似支付寶紅包、微信紅包等的監控預警甚至是秒級的,分鐘級確實是不夠用,不可能說發一個紅包,1分鐘還沒有搶到,用戶會一直不停地戳屏幕,對系統帶來的壓力反而更大。
所以,這個時候我們就需要做一些擴展性的工作。最簡單的就是按業務擴展,一個小公司大概到十個、幾十個人左右,大概會做這樣的工作。如網站上的表現層,一開始頁面代碼都耦合在一起,然后我們會把詳情頁、搜索頁、團購區分,包括域名也會區分開。慢慢的業務多了之后,會把普通購物、團購、虛擬業務等剝離開來,從數據庫層來說,慢慢會把產品、用戶、訂單及其他一些業務數據等都進行分拆,主要是從物理上隔離開來。這個時候總體來說,架構本身變化不大,只是簡單的切分一下。當然這個時候產品、用戶、訂單等這個層面的劃分要有一定的規則和邊界。
電商網站演進之路
一個小網站變成一個大網站是一個架構演進的過程。舉例子來說,現在有一個老師,他講課的時候有很多學生都在同一個教室,這邊是一年級的,這邊是二年級的,這邊是三年級的,先給一年級講課二三年級的自己復習做功課,再給二年級的講課一三年級的復習做功課,在前期還能忙過來。但到了一定的時候,學生開始多了,教室坐不下了。業務也多了,我不光教語文、數學,我還要教體育、音樂、美術了,這個時候學校的規模大了。學校的規模大了之后,相關的配套設施就要跟著上去。這個時候要有校長、主任、班主任、音樂英語老師…有后勤的,甚至是保潔阿姨,甚至還有食堂了,和大學一樣了。
這個例子可以類比我們的架構變化。首先是業務變化,這相當于課程的變化;第二,架構變化,就相當于學校的規模大了之后,相關的配套設施都要跟上;第三個就是人員擴大了,就是我們的開發維護人員多了,這個時候我們不可能還像以前那樣,幾百號人在那里弄一個工程,你必須要進行分割剝離。
那么我們怎么把它做大?沒有別的路,就是拆分擴展,而且擴展不能是簡單的橫向擴展,不能說來一個業務就簡單地按業務來拆。因為可能一個業務量就非常的大,比如說我們的團購體量就非常大,僅僅團購業務的訂單量已經很大了,自己本身不能再擴了那怎么辦?從我的總結來說就是拆,把大的拆成小的,不斷地拆。包括現在提到的微服務,當然現在微服務還沒有明確的定義。這和我們的SOA架構是分不開的,只不過是一種形式而已。
現在IT界有很多的概念,就好像敏捷一樣,兩個星期做一個迭代,以前是一個項目做好幾個月。一開始我不太理解什么是敏捷,有什么變化呢?我覺得大概就是把大的拆成小的,以前是做兩個月的,我們把它分成N個兩個星期的,在兩個星期的迭代當中,你該做的還是逃不了,你還是要做需求分析,做設計,做開發,做測試,做上線。這個過程沒有變,只是說把任務給拆分小了。
在拆的時候,從表現層來看,可以從UI展示和UI邏輯上進行拆分。在邏輯層上,比如說你要提交訂單,在提交訂單后面的操作包括有訂單的服務、接口,這是我們的后端業務邏輯控制層。拆的時候,原來業務邏輯和數據訪問都在一層,大家知道,以前在JSP上可以在頁面上直接去連數據庫進行DB操作。 我們從業務邏輯層進行拆分,拆分成控制層和Service層及數據庫操作層。Service層就涉及到剛剛講到的微服務,而Service層也分為復雜的Service和簡單的Service。基礎Service層相對來說業務邏輯比較單一,但是又相對比較完整,聚合Service則包括了完整的業務邏輯。
我們的技術團隊也是從一個小團隊發展到上千人的規模,網站也經歷這樣一個變革,剛開始的時候就是一個簡單的MVC架構,后來這個架構不適應業務的發展和人員規模了。在三五百人的時候我們還是那個架構,很多人在維護一個大的工程項目,經常出問題。
而且剛剛開始的時候是沒有無線端的,無線端也是近幾年才開始做起來的。
后來要做無線端,怎么辦?把PC端的代碼包復制一份給無線端用,這個時候問題就不斷的來了,因為PC端的邏輯不可能完全符合無線端的要求。
架構演進的準備工作
這樣我們開始做比較大的架構層面的規劃,最大的一個就是業務邏輯層的拆分-SOA服務化,另一個是DB層的水平拆庫。拆分之后,理論上它就具有了無限的擴展能力,比如說訂單庫,我可以把它按照一定的維度,去拆成很多的訂單庫。
拆分和解耦是分不開的,一方面是代碼和業務的解耦,另一方面對人員和工作來說也是一種解耦。同樣還要做一些異步工作,因為以前業務特別重,下單流程非常長,操作步驟非常多,但是很多東西并不是用戶都要馬上關注的。比如說下單給用戶送積分,這個積分并不是說下單的時候必須馬上就要給到客戶的,可以稍微延遲幾秒甚至是幾分鐘,它不應該影響下單業務。但是如果因為積分出現問題,導致下單出現問題那就是本末倒置了。因此我們做了異步,它掛了,我們可以做補償。
當然有一些是不能做異步的,比如說積分兌換商品的訂單,下單時要去扣積分,因為積分就是錢,我們還有禮品卡支付,這個是不能做異步的。我記得以前有過一個例子,好像是一個網站用積分可以充話費,結果話費充成功了,積分沒扣,就變成了可以無限的充。現在是互聯網時代,信息散布的很快,據說一兩個小時就是幾個億的損失。還有讀寫分離,讀和寫是可以完全分開的。為了保證下單的流暢,我們把讀和寫分開,在不同的庫里進行讀和寫,這樣可以很大地減輕下單壓力。
核心Service規劃
這張圖是我們核心Service的規劃。大家想象一下一個電商網站有哪些基本特點,哪怕你的網站上只有一個頁面一個商品,比如說你只賣蘋果手機或者是小米手機,網站就一個頁面顯示這個商品,你點商品就可以直接進行購物。首先它要有商品的描述信息,第二是商品的價格,第三是商品的庫存,當然庫存數字你可以不顯示,有就賣,沒有就不賣,以上這些是商品基本的信息。
有了商品的信息,如果你想買這個商品,你要先注冊用戶,注冊用戶之后要登陸,這是用戶信息;然后你就可以下單了,生成一個訂單,訂單之后是對訂單進行相應的物流信息跟蹤。同時也可以做線上支付,當然你也可以只做貨到付款不做線上支付。
這是一個電商網站最基本的核心要素。一個是產品,第二個是用戶&支付,再就是訂單。這三大塊構成了一個電商網站最核心的三個部分,這就是我們核心Service的架構規劃,抓住核心是服務化的重要理念。從Service角度來說,產品服務、價格服務相對來說比較簡單和單一,對產品的價格來說用戶主要是查;但是對訂單來說,我們把庫存放到訂單這個層面來,而不是放到產品上,是因為庫存和訂單是息息相關的,生成訂單的時候要扣庫存的,庫存不足的話,訂單是無法完成的。
剛開始的時候我們沒有服務化,訂單有兩個問題:第一個是寫,生成訂單。寫的業務是很多的,比如說加購物車生成訂單,充值是一種業務的訂單,電子卡是一種業務的訂單,都在寫;第二個是讀,訂單寫了之后,用戶要來讀、后端的客服商家也在讀。
讀不是簡單的僅僅讀訂單表,還有很多其他的關聯表。比如說抽獎有抽獎系統有自己的很多表,抽完獎給用戶發一個獎品,就生成一個訂單,但抽獎系統要知道哪一個用戶是通過什么樣的方式獲得這個訂單、訂單里有什么東西,必然要關聯查詢。所以訂單查詢是非常復雜的,有無數的點可以查,而且這個查詢一定是有無數張表可以關聯查的,甚至是跨表、跨庫的。
那么Service怎么做呢?如果說我把所有的訂單相關表都關聯起來,都納入到Service范圍的話,基本上可以把70%-80%的業務都納入其中了。因為幾乎所有的業務都要圍繞訂單來轉,所以Service化一定要有一個邊界。邊界是什么?比如說我剛剛說的,你抽獎的信息我關不關心呢?如果說我關心的話,你的抽獎業務就納入進來了。這就是一個邊界,所以我只能只關心我的訂單,這就是訂單的邊界。
上圖這句話我覺得說的非常好。一個出色的演講一定要很短,一定也要很長。這對我們來說是非常有意義的。這一塊來講怎么樣很短,怎么樣又很長?
回到核心Service的規則。既要很短,又要很長,比如說訂單的生成,它有很多的業務,要給客戶積分,要生成訂單表數據,要把抵用券扣掉,還有相關的支付信息,這些業務是不能剝離開來,必須要融合在一起,這當中要包含所有的能想到的業務場景,因此在訂單生成業務上,我們要做到足夠長,把所有的業務都包含進來。有一些是需要同步的,有一些是需要異步的,但是無論是同步還是異步的,我們都要納入進來。
但對查詢來說,業務是可以分開的,比如商品詳情頁,可能先是調商品的基本信息Service,然后再去調價格信息Service,然后再調用庫存服務Service,生成一個頁面需要很多的服務,這些服務可以是各自獨立的,所以在查詢Service上,我們可以做的很短。價格、庫存等都可以做成獨立的業務Service單元。
當然獨立并不一定是最簡單,它也要有自己完整的業務邏輯。比如庫存并不是簡單地看庫存表里的數字是大于0還是小于0的問題,比如是說某一個地方銷售不銷售,或者說我們盡管有庫存,我們現在是不賣它的,這些都是庫存,庫存不是一個簡單的數字,如果說這個商品暫時不賣,我就顯示說無庫存,但是我的倉庫里是有實物庫存的。
我理解所謂的微服務,在底層這一塊更多像微服務,微服務是不是拆分的越多越好,也不一定。比如說庫存,如果看這個商品有沒有庫存,首先你調一個服務看這個商品是不是在這個區域里賣,再調一個服務看商品是不是上架,再調一個服務看庫存是不是大于0,那就太多了。
舉個例子,打開一個詳情頁會調用很多的服務,類目信息、商品信息、價格信息、庫存信息、評論等等。這么多服務,怎么保證性能呢,如果拆的非常細的話,僅一個庫存服務就可以拆成7、8個子服務,這樣的話服務就要調七八次,網絡交互也是七八次,單個Service性能再好又怎么樣呢?哪怕你的服務性能達到1毫秒,夠快了吧,你調用10次要10毫秒,調用100次要100毫秒,你的性能還是在下降,所以并不是越微越好,這個長和短的粒度要劃分好。記住兩個關鍵詞:邊界和粒度。
訂單水平拆庫
接下來我們談談數據庫。我們最早用的Oracle,很龐大,支持的量也很大,一般的業務量是沒有問題的。但是什么情況會出問題呢?一個是單點故障,數據庫一掛了,整個網站就全掛了,另外不支持水平擴展,包括它的存儲、性能、數據量,Oracle再厲害,它不可能幾百億、幾千億的數據都放進去。所以我們后來選擇了Mysql,對數據庫進行了水平拆分,這樣的話單點故障率會小一點,這么多的物理數據庫,掛一個,其他的還可以運行,不至于影響全局。同時做了水平拆分之后,擴展能力非常強,從理論上來說可以無限擴展,因為它無非就是加服務器,你只要加一些硬件就可以了。
那么水平拆分怎么拆?要考慮哪些因素?
比如說訂單,你第一要考慮業務場景,查詢訂單是哪些用戶:其一是前端的用戶;其二是后端的用戶商家和客服。
第二,它的存儲量,訂單的數據量是非常大的。但對商品和庫存來說,它是有一定的范圍的,不會無限的大,因為一個網站或者一個商店,你賣的SKU數量是有限的。一個大超市可能是幾萬個SKU,一個小門店可能是幾百個,它不會無限擴展的。
數據增量也是如此,一個大超市賣的SKU也就是幾萬個,電商平臺可能是百萬級千萬級,但是它也不是無限增長的,這更多取決于商家的體量,所以它的數據量即使有增長也是非常緩慢的。這和訂單不一樣,訂單是幾何式的增長。
再看讀和寫,訂單、庫存的讀和寫頻率都很高。但是對于商品、價格來講,讀肯定是很高的,因為不停地在瀏覽,但是寫是很少的,改價格的機率很低,不停地改商品信息的機率也是很低的。
另外是事務的一致性。對于訂單和庫存一定是要保持一致性,商品信息寫的話比較少,不太涉及到事務,除非是批量修改,相對來說事務性一致性稍微弱一些。
還有緩存,庫存可以有緩存,但是緩存的時間是很短的,庫存的緩存時效不可能是以天、以小時為級別的,幾分鐘級別已經是不錯了。很多時候前端顯示還是有庫存,后面可能已經沒有了,所以庫存有時效性的要求。但為了減輕數據庫壓力,在前端展示會有庫存的緩存,比如有時候大家會遇到,在瀏覽的時候發現它是有庫存的,但是下單就沒有了,那就是因為前端是緩存的,但是下單的是實時的庫存,已經沒有了。但對商品和價格信息來說,緩存時效就可以長一些,可以通過緩存技術減輕數據庫的壓力。
熱點數據也是一樣的。數據庫的水平拆分怎么拆,從哪些維度去拆,比如說訂單,可以有幾個維度,你可以根據訂單號去拆,根據產用戶、商家去拆。對響應速度來說,用戶要求響應速度是最高的;而對商家來說,用戶下完訂單之后,稍微延遲一會兒他也能接受。
如果按照用戶去拆,熱點數據的概率就很低,很難出現一個用戶一下子出現幾千個幾萬個訂單;但是如果按對商家來拆,有一些大的商家,一個雙十一可能幾個小時就有上千萬的單,這個量就非常大。
而對商品信息來說,如果說你的量沒有像天貓、淘寶級別的話,并且主要是靠緩存來讀,一般的電商網站,是不需要拆分的。
拆庫時怎么做壓測
在做訂單水平拆庫的時候,不可能網站停下來去做這個項目,所以我們說是飛機開的時候換發動機,在汽車跑的時候換輪胎。我們在做數據庫拆庫的時候要做壓測。怎么做壓測呢?
Oracle改Mysql的時候,當時我們對性能是沒有絕對的信心的,因為Mysql的健壯性沒有Oracle強大,有一次一個badsql直接把我們的一個mysql數據庫給搞掛了,對性能要求特別高,但是在業務層,我們很難去模擬。我們可以在Service或sql上一步一步的分析,一步一步的優化,但是畢竟有很多業務場景是模擬不出來的。
當時我們做了Tcpcopy壓測,原理就是把線上請求的包抓下來,放到測試環境中,測試的數據庫盡量保持和線上一致,保持環境一致。壓測會動態調整流量,把原來的流量比如說一小時千萬級的提升到億級的,提升了很多倍,主要是測試看能不能把數據庫壓垮,會不會出現問題。
當然這個場景也不可能完全覆蓋我們的現實應用場景,因為在線上抓包的時候,我們抓了一天,但這一天中數據庫的數據是不斷變化,不斷有insert和update,而線下的測試數據是一個靜態的數據,所以還有一些業務場景我們是模擬不到的。因此模擬結果和線上還是有一定的差距,但還是給我們吃了一顆很大的定心丸。
SOA中間件
我剛剛說了兩個,一個是Service化做了技術架構上的拆分,一個是做了數據庫的水平拆分。這是剛剛提到的準備工作,Service化和水平拆庫的同時,我們的很多中間件技術也發展起來了,因為你的量上來了、架構調整了,配套設施也要上來,不是說簡單的教室一拆分就完了,學校沒有保安,要上體育課沒有操場是不行的,因此沒有相應的中間件沒有是不行的。
SOA中間件本身也是一個有意思的發展,包括分布式服務SOA中間件、數據庫中間件、緩存平臺、消息中間件、任務調度中間件和全局配置中心等。日志和監控系統也非常有必要,這都是系統穩定的基礎。
還有實時的分析系統,比如說雙十一,大家都關注著淘寶的數字,那個數字是怎么出來的,一定是實時出來的,你不能說到了第二天才告訴人家前一天晚上1點的時候是什么樣的數據,一定是剛過1點就馬上就都出來了。
同樣還要做灰度發布,什么叫高可用,就是不出問題系統一直處于可用狀態。但我們還要發布啊,發布的時候怎么辦,所以灰度發布的價值就體現出來了,有了它我們的系統就有了100%可用的理論可能。
這是我們的一個簡單的架構圖。提交訂單的時候,可以同步也可以異步提交,異步走的是秒殺系統,它不是提交之后馬上生成訂單,而是要有排隊系統進行排隊的。我剛剛在前面還說過負載均衡,我們開發了自己的SOA中間件做負載均衡,它有自己的邏輯控制,購物流程到到訂單服務是通過SOA中間件做負載均衡和調用的。
同時我們還有數據庫中間件,我們和數據庫的交互怎么辦?一個訂單查詢,如何定位到它所在的數據庫。如果是根據用戶維度拆庫,用戶來查詢馬上可以定位到相應的數據庫,但是商家來查詢怎么辦?他的訂單可能是覆蓋所有的數據庫,這個時候需要做一些聚合、排序。這就要通過數據庫中間件,它對前端是透明的,它去做一些排序等,應用層只須常規的寫自己的SQL就行了。
同樣,我們還有消息中間件,比如前面提到的下單后送積分,就可以通過消息異步處理。
這是我們的核心交易架構,我們如何讓它更完美一些,怎么讓它的穩定性更高一些?我們有前臺用戶,前臺用戶作為普通消費者去下單、查詢,同時也有很多后臺的操作。
比如對消費者來說,下完單后要做支付,當然他可能會訂單取消,要把訂單變成取消狀態;再一個他會修改收貨地址,也就是這些簡單的幾個update操作。而后端的,運營也好,客服也好,對這個訂單是有很多的操作的。可能還有審核系統,還有發貨、出庫等等的系統都要對訂單進行操作,因此我們后端的反而是更復雜的。后端的操作必然會影響到數據庫,如果不注意也會出現很多的問題,把數據庫夯住了,影響了前端的交易。
Service層可以不斷的拆,但從用戶層角度來說,還是要考慮前端和后端的拆開。比如代碼可以前端一套,后端一套,把它物理隔離開。我們主要目的是保前端的交易,后端系統稍微延遲一點沒有問題的,但是大家看到,前后端代碼物理上雖然隔開了,但是DB還是在一起,后端寫的代碼把數據庫搞掛了,前端還是照樣掛,這是一個很大的問題。
多活機房架構
最后說一下多活,多活的架構比較大,可以專門作為一個主題來,我這里只是給大家引申一下。
剛剛講到,前端用戶、后端用戶盡管代碼什么的可以隔開,但是DB這一層還是在一起。因此我們也要想辦法把DB分開,但這個時候,兩個DB的數據要保持同步,當然我今天說的只是一個思路而不是解決方案。
這是我在網上搜來的一張雙活的圖,想想雙活和多活其實是一樣的。我們可以有不同的機房,也可以在一個機房內部有多個獨立的單元,多個機房或單元物理獨立。這樣的話,一定要有一個統一的數據中心,這兩個數據要同步,因為前端用戶下完單之后,商家在看訂單的時候,不可能要看這邊的訂單到這個系統,要看那邊訂單到另外一個系統去看,因此必須要有數據中心。如果說我們有多個機房,可能是三個五個,像淘寶的級別就是非常多的機房。我理解它一定要有一個數據中心把數據匯總起來。
當然我這是從應用層來說,從應用隔離的角度去看的。多活的目的不是簡單的隔離,它考慮的是一旦發生地震、災害等如何保證不出問題,這個時候數據中心對多活也是必要的。
但數據中心我是不是可以作為后端應用來使用呢?后端的應用走數據中心,因為它對數據的實時性要求相對不是特別高,而前端只保證核心交易業務,后端保證非核心交易業務,這是多活應用架構拆分的思路。
轉載于:https://www.cnblogs.com/davidwang456/articles/8213874.html
總結
以上是生活随笔為你收集整理的1号店交易系统架构如何向「高并发高可用」演进的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 蚂蚁金服11.11:支付宝和蚂蚁花呗的技
- 下一篇: 《京东技术解密》——海量订单处理