微服務(wù)設(shè)計的一個關(guān)鍵是數(shù)據(jù)庫設(shè)計,基本原則是每個服務(wù)都有自己單獨(dú)的數(shù)據(jù)庫,而且只有微服務(wù)本身可以訪問這個數(shù)據(jù)庫。它是基于下面三個原因。
理想的設(shè)計是你的數(shù)據(jù)庫只有你的服務(wù)能訪問,你也只調(diào)用自己數(shù)據(jù)庫中的數(shù)據(jù),所有對別的微服務(wù)的訪問都通過服務(wù)調(diào)用來實現(xiàn)。當(dāng)然,在實際應(yīng)用中,單純的服務(wù)調(diào)用可能不能滿足性能或其他要求,不同的微服務(wù)都多少需要共享一些數(shù)據(jù)。
微服務(wù)之間的數(shù)據(jù)共享可以有下四種方式。
有一些靜態(tài)的數(shù)據(jù)庫表,例如國家,可能會被很多程序用到,而且程序內(nèi)部需要對國家這個表做連接(join)生成最終用戶展示數(shù)據(jù),這樣用微服務(wù)調(diào)用的方式就效率不高,影響性能。一個辦法是在每個微服務(wù)中配置一個這樣的表,它是只讀的,這樣就可以做數(shù)據(jù)庫連接了。當(dāng)然你需要保證數(shù)據(jù)同步。這個方案在多數(shù)情況下都是可以接受的,因為以下兩點(diǎn):
如果你需要讀取別的數(shù)據(jù)庫里的動態(tài)業(yè)務(wù)數(shù)據(jù), 理想的方式是服務(wù)調(diào)用。如果你只是調(diào)用其他微服務(wù)做一些計算,一般情況下性能都是可以接受的。如果你需要做數(shù)據(jù)的連接,那么你可以用程序代碼來做,而不是用SQL語句。如果測試之后性能不能滿足要求,那你可以考慮在自己的數(shù)據(jù)庫里建一套只讀數(shù)據(jù)表。數(shù)據(jù)同步方式大致有兩種。如果是事件驅(qū)動方式,就用發(fā)消息的方式進(jìn)行同步,如果是RPC方式,就用數(shù)據(jù)庫本身提供的同步方式或者第三方同步軟件。
通常情況下,你可能只需要其他數(shù)據(jù)庫的幾張表,每張表只需要幾個字段。這時,其他數(shù)據(jù)庫是數(shù)據(jù)的最終來源,控制所有寫操作以及相應(yīng)的業(yè)務(wù)驗證邏輯,我們叫它主表。你的只讀庫可以叫從表。 當(dāng)一條數(shù)據(jù)寫入主表后,會發(fā)一條廣播消息,所有擁有從表的微服務(wù)監(jiān)聽消息并更新只讀表中的數(shù)據(jù)。但這時你要特別小心,因為它的危險性要比靜態(tài)表大得多。第一它的表結(jié)構(gòu)變更會更頻繁,而且它的變更完全不受你控制。第二業(yè)務(wù)數(shù)據(jù)不像靜態(tài)表,它是經(jīng)常更新的,這樣對數(shù)據(jù)同步的要求就比較高。要根據(jù)具體的業(yè)務(wù)需求來決定多大的延遲是可以接受的。
另外它還有兩個問題:
除非你能用服務(wù)調(diào)用(沒有本地只讀數(shù)據(jù)庫)的方式完成所有功能,不然不管你是用RPC方式還是事件驅(qū)動方式進(jìn)行微服務(wù)集成,上面提到的問題都是不可避免的。但是你可以通過合理規(guī)劃數(shù)據(jù)庫更改,來減少上面問題帶來的影響,下面將會詳細(xì)講解。
這是最復(fù)雜的一種情況。一般情況下,你有一個表是主表,而其他表是從表。主表包含主要信息,而且這些主要信息被復(fù)制到從表,但微服務(wù)會有額外字段需要寫入從表。這樣本地微服務(wù)對從表就既有讀也有寫的操作。而且主表和從表有一個先后次序的關(guān)系。從表的主鍵來源于主表,因此一定先有主表,再有從表。
上圖是例子。假設(shè)我們有兩個與電影有關(guān)的微服務(wù),一個是電影論壇,用戶可以發(fā)表對電影的評論。另一個是電影商店。“movie”是共享表,左邊的一個是電影論壇庫,它的“movie”表是主表。右邊的是電影商店庫,它的“movie”表是從表。它們共享“id”字段(主鍵)。主表是數(shù)據(jù)的主要來源,但從表里的“quantity”和“price”字段主表里面沒有。主表插入數(shù)據(jù)后,發(fā)消息,從表接到消息,插入一條數(shù)據(jù)到本地“movie”表。并且從表還會修改表里的“quantity”和“price”字段。在這種情況下,要給每一個字段分配一個唯一源頭(微服務(wù)),只有源頭才有權(quán)利主動更改字段,其他微服務(wù)只能被動更改(接收源頭發(fā)出的更改消息之后再改)。在本例子中, “quantity”和“price”字段的源頭是右邊的表,其他的字段的源頭都是左邊的表。本例子中“quantity”和“price”只在從表中存在,因此數(shù)據(jù)寫入是單向的,方向是主表到從表。如果主表也需要這些字段,那么它們還要被回寫,那數(shù)據(jù)寫入就變成雙向的。
這種方式是要絕對禁止的。生產(chǎn)環(huán)境中的許多程序錯誤和性能問題都是由這種方式產(chǎn)生的。上面的三種方式由于是另外新建了本地只讀數(shù)據(jù)庫表,產(chǎn)生了數(shù)據(jù)庫的物理隔離,這樣一個數(shù)據(jù)庫的性能問題不會影響到另一個。另外,當(dāng)主庫中的表結(jié)構(gòu)更改時,你可以暫時保持從庫中的表不變,這樣程序還可以運(yùn)行。如果直接訪問別人的庫,主庫一修改,別的微服務(wù)程序馬上就會報錯。
從上面的論述可以看出,數(shù)據(jù)庫表結(jié)構(gòu)的修改是一個影響范圍很廣的事情。在微服務(wù)架構(gòu)中,共享的表在別的服務(wù)中也會有一個只讀的拷貝。現(xiàn)在當(dāng)你要更改表結(jié)構(gòu)時,還需要考慮到對別的微服務(wù)的影響。當(dāng)在單體(Monolithic)架構(gòu)中,為了保證程序部署能夠回滾,數(shù)據(jù)庫的更新是向后兼容的。需要兼容性的另一個原因是支持藍(lán)綠發(fā)布(Blue-Green Deployment)。在這種部署方式中,你同時擁有新舊版本的代碼,由負(fù)載均衡來決定每一個請求指向那個版本。它們可以共享一個數(shù)據(jù)庫(這就要求數(shù)據(jù)庫是向后兼容的),也可以使用不同的數(shù)據(jù)。數(shù)據(jù)庫的更新簡單來講有以下幾種類型:
向后兼容的數(shù)據(jù)庫更新的好處是,當(dāng)程序部署出現(xiàn)問題時,如需進(jìn)行回滾。只要回滾程序就行了,而不必回滾數(shù)據(jù)庫。回滾時一般只回滾一個版本。凡是需要刪除的表或字段在本次部署時都不做修改,等到一個或幾個版本之后,確認(rèn)沒有問題了再刪除。它的另一個好處就是不會對其他微服務(wù)中的共享表產(chǎn)生立刻的直接影響。當(dāng)本微服務(wù)升級后,其他微服務(wù)可以評估這些數(shù)據(jù)庫更新帶來的影響再決定是否需要做相應(yīng)的程序或數(shù)據(jù)庫修改。
微服務(wù)的一個難點(diǎn)是如何實現(xiàn)跨服務(wù)的事物支持。兩階段提交(Two-Phase Commit)已被證明性能上不能滿足需求,現(xiàn)在基本上沒有人用。被一致認(rèn)可的方法叫Saga。它的原理是為事物中的每個操作寫一個補(bǔ)償操作(Compensating Transaction),然后在回滾階段挨個執(zhí)行每一個補(bǔ)償操作。示例如下圖,在一個事物中共有3個操作T1,T2,T3。每一個操作要定義一個補(bǔ)償操作,C1,C2,C3。事物執(zhí)行時是按照正向順序先執(zhí)行T1,當(dāng)回滾時是按照反向順序先執(zhí)行C3。 事物中的每一個操作(正向操作和補(bǔ)償操作)都被包裝成一個命令(Command),Saga執(zhí)行協(xié)調(diào)器(Saga Execution Coordinator (SEC))負(fù)責(zé)執(zhí)行所有命令。在執(zhí)行之前,所有的命令都會按順序被存入日志中,然后Saga執(zhí)行協(xié)調(diào)器從日志中取出命令,依次執(zhí)行。當(dāng)某個執(zhí)行出現(xiàn)錯誤時,這個錯誤也被寫入日志,并且所有正在執(zhí)行的命令被停止,開始回滾操作。
Saga放松了對一致性(Consistency)的要求,它能保證的是最終一致性(Eventual Consistency),因此在事物執(zhí)行過程中數(shù)據(jù)是不一致的,并且這種不一致會被別的進(jìn)程看到。在生活中,大多數(shù)情況下,我們對一致性的要求并沒有那么高,短暫的不一致性是可以接收的。例如銀行的轉(zhuǎn)賬操作,它們在執(zhí)行過程中都不是在一個數(shù)據(jù)庫事物里執(zhí)行的,而是用記賬的方式分成兩個動作來執(zhí)行,保證的也是最終一致性。
Saga的原理看起來很簡單,但要想正確的實施還是有一定難度的。它的核心問題在于對錯誤的處理,要把它完全講明白需要另寫一遍文章,我現(xiàn)在只講一下要點(diǎn)。網(wǎng)絡(luò)環(huán)境是不可靠的,正在執(zhí)行的命令可能很長時間都沒有返回結(jié)果,這時,第一,你要設(shè)定一個超時。第二,因為你不知道沒有返回值的原因是,已經(jīng)完成了命令但網(wǎng)絡(luò)出了問題,還是沒完成就犧牲了,因此不知道是否要執(zhí)行補(bǔ)償操作。這時正確的做法是重試原命令,直到得到完成確認(rèn),然后再執(zhí)行補(bǔ)償操作。但這對命令有一個要求,那就是這個操作必須是冪等的(Idempotent),也就是說它可以執(zhí)行多次,但最終結(jié)果還是一樣的。
另外,有些操作的補(bǔ)償操作比較容易生成,例如付款操作,你只要把錢款退回就可以了。但有些操作,像發(fā)郵件,完成之后就沒有辦法回到之前的狀態(tài)了,這時就只能再發(fā)一個郵件更正以前的信息。因此補(bǔ)償操作不一定非要返回到原來的狀態(tài),而是抵消掉原來操作產(chǎn)生的效果。
我們原來的程序大多數(shù)都是單體程序,但現(xiàn)在要把它拆分成微服務(wù),應(yīng)該怎樣做才能降低對現(xiàn)有應(yīng)用的影響呢?
我們用上面的圖來做例子。它共有兩個程序,一個是“Styling app”,另一個是“Warehouse app”,它們共享圖中下面的數(shù)據(jù)庫,庫里有三張表,“core client”,“core sku”,“core item”。
假設(shè)我們要拆分出來一個微服務(wù)叫“client-service”,它需要訪問“core client”表。第一步,我們先把程序從原來的代碼里拆分出來,變成一個服務(wù). 數(shù)據(jù)庫不動,這個服務(wù)仍然指向原來的數(shù)據(jù)庫。其他程序不再直接訪問這個服務(wù)管理的表,而是通過服務(wù)調(diào)用或另建共享表來獲取數(shù)據(jù)。
第二步,再把服務(wù)的數(shù)據(jù)庫表拆分出來,這時微服務(wù)就擁有它自己的數(shù)據(jù)庫了,而不再需要原來的共享數(shù)據(jù)庫了。這時就成了一個真正意義上的的微服務(wù)。
上面只講了拆分一個微服務(wù),如果有多個需要拆分,則需一個一個按照上面講的方法依次進(jìn)行。
另外,Martin Fowler有一個很好的建議。那就是,當(dāng)你把服務(wù)從單體程序里拆分時,不要只想著把代碼拆分出來。因為現(xiàn)在的需求可能已經(jīng)跟原來有所不同,原先的設(shè)計可能也不太適用了。而且,技術(shù)也已更新,代碼也要作相應(yīng)的改造。更好的辦法是重寫原來的功能(而不是重寫原來的代碼),把重點(diǎn)放在拆分業(yè)務(wù)功能上,而不是拆分代碼上,用新的設(shè)計和技術(shù)來實現(xiàn)這個業(yè)務(wù)功能。
數(shù)據(jù)庫設(shè)計是微服務(wù)設(shè)計的一個關(guān)鍵點(diǎn),基本原則是每個微服務(wù)都有自己單獨(dú)的數(shù)據(jù)庫,而且只有微服務(wù)本身可以訪問這個數(shù)據(jù)庫。微服務(wù)之間的數(shù)據(jù)共享可以通過服務(wù)調(diào)用,或者主、從表的方式實現(xiàn)。在共享數(shù)據(jù)時,要找到合適的同步方式。在微服務(wù)架構(gòu)中,數(shù)據(jù)庫的修改影響廣泛,需要保證這種修改是向后兼容的。實現(xiàn)跨服務(wù)事物的標(biāo)準(zhǔn)方法是Saga。當(dāng)把單體程序拆分成微服務(wù)時,可以分步進(jìn)行,以減少對現(xiàn)有程序的影響。
創(chuàng)新互聯(lián)www.cdcxhl.cn,專業(yè)提供香港、美國云服務(wù)器,動態(tài)BGP最優(yōu)骨干路由自動選擇,持續(xù)穩(wěn)定高效的網(wǎng)絡(luò)助力業(yè)務(wù)部署。公司持有工信部辦法的idc、isp許可證, 機(jī)房獨(dú)有T級流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確進(jìn)行流量調(diào)度,確保服務(wù)器高可用性。佳節(jié)活動現(xiàn)已開啟,新人活動云服務(wù)器買多久送多久。
分享文章:微服務(wù)設(shè)計關(guān)鍵的難點(diǎn):微服務(wù)架構(gòu)的數(shù)據(jù)庫是如何設(shè)計的?-創(chuàng)新互聯(lián)
本文地址:http://www.chinadenli.net/article24/ijcce.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供電子商務(wù)、域名注冊、自適應(yīng)網(wǎng)站、全網(wǎng)營銷推廣、微信公眾號、網(wǎng)站內(nèi)鏈
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容