web應(yīng)用從單點(diǎn)向高并發(fā)架構(gòu)演變時(shí)往往遇到最大的問(wèn)題就是數(shù)據(jù)庫(kù)的分布式存儲(chǔ)。因?yàn)閣eb應(yīng)用本身就可以集群部署,但其所使用的數(shù)據(jù)庫(kù)確是單點(diǎn)的。如果一個(gè)web應(yīng)用開(kāi)始的時(shí)候沒(méi)有考慮數(shù)據(jù)庫(kù)的分布式架構(gòu),那么等到要進(jìn)行數(shù)據(jù)庫(kù)集群改造時(shí)會(huì)發(fā)現(xiàn)困難重重,此時(shí)通常的做法是將原系統(tǒng)拆分成多個(gè)子系統(tǒng),然后每個(gè)子系統(tǒng)訪問(wèn)一個(gè)數(shù)據(jù)庫(kù),這幾乎重寫了整個(gè)系統(tǒng)(如果這還不能滿足需求,大型企業(yè)接下來(lái)會(huì)增加數(shù)據(jù)存儲(chǔ)總線)。很多廠商都是這么做的,包括淘寶。如果你開(kāi)始你能夠考慮到數(shù)據(jù)庫(kù)集群,并且這種集群的設(shè)置并不增加開(kāi)發(fā)工作量和難度,那么將來(lái)可以為企業(yè)節(jié)省大量人力物力。
一般來(lái)說(shuō)我們?cè)陂_(kāi)發(fā)一個(gè)應(yīng)用時(shí),需要對(duì)應(yīng)用進(jìn)行分層,一般分為界面層、業(yè)務(wù)層、存儲(chǔ)層,這有利于開(kāi)發(fā),也有利于將來(lái)部署運(yùn)行。這里并不特指Web程序,桌面應(yīng)用也是一樣。應(yīng)用系統(tǒng)從單點(diǎn)運(yùn)行演變?yōu)榉植际綉?yīng)用一般需要經(jīng)歷下面3個(gè)步驟。
圖(1),系統(tǒng)的各層屬于一個(gè)應(yīng)用,彼此之間以API的方式(進(jìn)程內(nèi))調(diào)用,可以滿足小并發(fā)量需求,一般的桌面應(yīng)用使用此種運(yùn)行方式。圖(2)系統(tǒng)的業(yè)務(wù)層被拆分,拆分成許多獨(dú)立的子系統(tǒng)(或同一系統(tǒng)的業(yè)務(wù)處理模塊單獨(dú)運(yùn)行,部署多個(gè)),前端界面和業(yè)務(wù)層以RPC進(jìn)行調(diào)用,業(yè)務(wù)層和存儲(chǔ)層以API的方式(進(jìn)程內(nèi))調(diào)用,此種運(yùn)行方式可以滿足中、高并發(fā)量的處理請(qǐng)求。圖(3)就是分布式的最終狀態(tài)了,所有功能都進(jìn)行集群部署,層與層之間都是RPC調(diào)用。
關(guān)于分布式,這里不再多說(shuō),本文主要討論的是存儲(chǔ)架構(gòu)的問(wèn)題。我們知道最簡(jiǎn)單的一個(gè)Web應(yīng)用就可以進(jìn)行圖(2)那樣的部署(如果需要登陸,那么還需采用session共享技術(shù)),這是因?yàn)閃eb應(yīng)用本身界面層和業(yè)務(wù)層默認(rèn)就是http調(diào)用,屬于遠(yuǎn)程調(diào)用的一種。我們假設(shè)有一個(gè)電子商務(wù)類應(yīng)用,運(yùn)行方式如下圖:
因?yàn)閃eb應(yīng)用本身界面是由頁(yè)面組成,并且可以緩存,我們也可以把界面層看成進(jìn)行了集群部署。所以說(shuō),對(duì)于一個(gè)簡(jiǎn)單的web應(yīng)用是可以很容易做到界面層和業(yè)務(wù)處理層集群部署和運(yùn)行的。那么隨著業(yè)務(wù)量增大和并發(fā)請(qǐng)求的增加,一個(gè)web應(yīng)用最先遇到的瓶頸就是存儲(chǔ)。從開(kāi)發(fā)角度最容易看出這個(gè)問(wèn)題,一個(gè)默認(rèn)web應(yīng)用,在訪問(wèn)數(shù)據(jù)庫(kù)時(shí)都是使用同一條數(shù)據(jù)庫(kù)連接(或同一個(gè)連接池),只能指向同一個(gè)數(shù)據(jù)庫(kù)。所以,如果我們能夠在一個(gè)web應(yīng)用中,讓業(yè)務(wù)層按需建立多個(gè)數(shù)據(jù)庫(kù)連接,那么也就實(shí)現(xiàn)了存儲(chǔ)的集群化。
一般來(lái)說(shuō)一個(gè)應(yīng)用遇到存儲(chǔ)瓶頸,可以通過(guò)緩存進(jìn)行優(yōu)化,如果緩存也不能滿足情況,就只能拆分?jǐn)?shù)據(jù)庫(kù)了。拆分?jǐn)?shù)據(jù)庫(kù)一般分為兩步走,首先是數(shù)據(jù)庫(kù)按業(yè)務(wù)垂直拆分,不要業(yè)務(wù)模塊的數(shù)據(jù)存儲(chǔ)到不同的數(shù)據(jù)庫(kù),這也稱為數(shù)據(jù)庫(kù)的垂直拆分,如下圖(1)。這是指同一個(gè)應(yīng)用來(lái)說(shuō)的,微服務(wù)架構(gòu)本身包括多種應(yīng)用,每種應(yīng)用有自己的數(shù)據(jù)庫(kù)訪問(wèn)模塊,每種應(yīng)用又可以包括多個(gè)業(yè)務(wù)處理模塊,垂直拆分后,每個(gè)業(yè)務(wù)模塊對(duì)應(yīng)一個(gè)數(shù)據(jù)庫(kù)。
因?yàn)槲⒎?wù)架構(gòu)本身就提倡將大應(yīng)用拆分成多個(gè)子服務(wù),每個(gè)應(yīng)用有自己的數(shù)據(jù)庫(kù),這已經(jīng)相當(dāng)于進(jìn)行了一次存儲(chǔ)的垂直拆分。再進(jìn)一步垂直拆分,一個(gè)子服務(wù)又可以使用多個(gè)數(shù)據(jù)庫(kù),到這一步,一般的高并發(fā)量就足夠處理了。但是一個(gè)系統(tǒng)往往有些模塊會(huì)持續(xù)不斷的增加數(shù)據(jù),比如電子商務(wù)中的訂單模塊,這會(huì)導(dǎo)致單表數(shù)據(jù)非常大。時(shí)間久會(huì)給查詢和計(jì)算帶來(lái)嚴(yán)重瓶頸,這個(gè)時(shí)候就需要數(shù)據(jù)庫(kù)水平拆分登場(chǎng)了,如圖(2),數(shù)據(jù)庫(kù)水平拆分是把之前某個(gè)業(yè)務(wù)處理模塊對(duì)應(yīng)的一個(gè)數(shù)據(jù)庫(kù),再按水平方向拆分,使一個(gè)業(yè)務(wù)處理模塊對(duì)應(yīng)操作幾個(gè)數(shù)據(jù)庫(kù),使原來(lái)一張表中的數(shù)據(jù)按規(guī)則存儲(chǔ)到幾個(gè)庫(kù)中。
水平拆分的前提條件是拆分的相關(guān)表必須屬于同一個(gè)聚合(領(lǐng)域驅(qū)動(dòng)中的聚合)。聚合中的根就是拆分依據(jù)的主表,我們根據(jù)主表數(shù)據(jù)計(jì)算規(guī)則進(jìn)行數(shù)據(jù)庫(kù)拆分。這種拆分方式非常重要,因?yàn)檫@種拆分方式形成的數(shù)據(jù)庫(kù)橫向集群支持?jǐn)?shù)據(jù)庫(kù)級(jí)事務(wù)。比如說(shuō)一個(gè)用戶聚合,包括用戶、資金賬戶、聯(lián)系人3張表。那么用戶就是主表,此時(shí)如果我們按用戶ID拆分?jǐn)?shù)據(jù)庫(kù),你會(huì)發(fā)現(xiàn)雖然是集群存儲(chǔ),但當(dāng)某個(gè)用戶訪問(wèn)自己的數(shù)據(jù)時(shí),這些數(shù)據(jù)始終在同一個(gè)庫(kù),同一個(gè)庫(kù)就支持?jǐn)?shù)據(jù)庫(kù)事務(wù)。