主頁 > 軟體設計 > 微服務的資料庫設計

微服務的資料庫設計

2020-09-15 07:51:57 軟體設計

單獨的資料庫:

微服務設計的一個關鍵是資料庫設計,基本原則是每個服務都有自己單獨的資料庫,而且只有微服務本身可以訪問這個資料庫,它是基于下面三個原因,

  • 優化服務介面:微服務之間的介面越小越好,最好只有服務呼叫介面(RPC或訊息),沒有其他介面,如果微服務不能獨享自己的資料庫,那么資料庫也變成了介面的一部分,這大大拓展了介面范圍,
  • 錯誤診斷:生產環境中的錯誤大部分都是和資料庫有關的,要么是資料出了問題,要么是資料庫的使用方式出了問題,當你不能完全控制資料庫的訪問時,會有各種各樣的錯誤發生,它可能是別的程式直接連到你的資料庫或者是其他部門直接用客戶端訪問資料庫的資料,而這些都是在程式中查不到的,增加了錯誤排查難度,如果是程式中的問題,只要修改了代碼,那么這個錯誤就不會再有,而上面提到的錯誤,你永遠都沒法預測它們什么時候還會再次發生,
  • 性能調優:性能調優也是一樣,你需要對資料庫有全權控制才能保證它的性能,如果其他部門一定要訪問資料庫,而且只是查詢的話,那么可以另外創建一份只讀資料庫,讓他們在另一個庫中查詢,這樣才不會影響到你的庫,

理想的設計是你的資料庫只有你的服務能訪問,你也只呼叫自己資料庫中的資料,所有對別的微服務的訪問都通過服務呼叫來實作(請參閱“微服務之間呼叫的最佳設計“),當然,在實際應用中,單純的服務呼叫可能不能滿足性能或其他要求,不同的微服務都多少需要共享一些資料,

共享資料:

微服務之間的資料共享可以有下四種方式,

靜態表:

有一些靜態的資料庫表,例如國家,可能會被很多程式用到,而且程式內部需要對國家這個表做連接(join)生成最終用戶展示資料,這樣用微服務呼叫的方式就效率不高,影響性能,一個辦法是在每個微服務中配置一個這樣的表,它是只讀的,這樣就可以做資料庫連接了,當然你需要保證資料同步,這個方案在多數情況下都是可以接受的,因為以下兩點:

  1. 靜態的資料庫表結構基本不變:因為一旦表結構變了,你不但要更改所有微服務的資料庫表,還要修改所有微服務的程式,
  2. 資料庫表中的資料變化不頻繁:這樣資料同步的作業量不大,另外當你同步資料庫時總會有延遲,如果資料變化不頻繁那么你有很多同步方式可供選擇,

只讀業務資料訪問:

如果你需要讀取別的資料庫里的動態業務資料, 理想的方式是服務呼叫,如果你只是呼叫其他微服務做一些計算,一般情況下性能都是可以接受的,如果你需要做資料的連接,那么你可以用程式代碼來做,而不是用SQL陳述句,如果測驗之后性能不能滿足要求,那你可以考慮在自己的資料庫里建一套只讀資料表,資料同步方式大致有兩種,如果是事件驅動方式,就用發訊息的方式進行同步,如果是RPC方式,就用資料庫本身提供的同步方式或者第三方同步軟體,

通常情況下,你可能只需要其他資料庫的幾張表,每張表只需要幾個欄位,這時,其他資料庫是資料的最終來源,控制所有寫操作以及相應的業務驗證邏輯,我們叫它主表,你的只讀庫可以叫從表, 當一條資料寫入主表后,會發一條廣播訊息,所有擁有從表的微服務監聽訊息并更新只讀表中的資料,但這時你要特別小心,因為它的危險性要比靜態表大得多,第一它的表結構變更會更頻繁,而且它的變更完全不受你控制,第二業務資料不像靜態表,它是經常更新的,這樣對資料同步的要求就比較高,要根據具體的業務需求來決定多大的延遲是可以接受的,

另外它還有兩個問題:

  1. 資料的容量:資料庫中的資料量是影響性能的主要因素,因為這個資料是外來的,不利于掌握它的流量規律,很難進行容量規劃,也不能更好地進行性能調優,
  2. ** 介面外泄**:微服務之間的介面本來只有服務呼叫介面,這時你可以對內部程式和資料庫做任何更改,而不影響其他服務,現在資料庫表結構也變成了介面的一部分,介面一旦發布之后,基本是不能更改的,這大大限制了你的靈活性,幸運的是因為另外建了一套表,有了一個緩沖,當主表修改時,從表也許不需要同步更新,

除非你能用服務呼叫(沒有本地只讀資料庫)的方式完成所有功能,不然不管你是用RPC方式還是事件驅動方式進行微服務集成,上面提到的問題都是不可避免的,但是你可以通過合理規劃資料庫更改,來減少上面問題帶來的影響,下面將會詳細講解,

讀寫業務資料訪問:

這是最復雜的一種情況,一般情況下,你有一個表是主表,而其他表是從表,主表包含主要資訊,而且這些主要資訊被復制到從表,但微服務會有額外欄位需要寫入從表,這樣本地微服務對從表就既有讀也有寫的操作,而且主表和從表有一個先后次序的關系,從表的主鍵來源于主表,因此一定先有主表,再有從表,

file

圖片來源

上圖是例子,假設我們有兩個與電影有關的微服務,一個是電影論壇,用戶可以發表對電影的評論,另一個是電影商店,“movie”是共享表,左邊的一個是電影論壇庫,它的“movie”表是主表,右邊的是電影商店庫,它的“movie”表是從表,它們共享“id”欄位(主鍵),主表是資料的主要來源,但從表里的“quantity”和“price”欄位主表里面沒有,主表插入資料后,發訊息,從表接到訊息,插入一條資料到本地“movie”表,并且從表還會修改表里的“quantity”和“price”欄位,在這種情況下,要給每一個欄位分配一個唯一源頭(微服務),只有源頭才有權利主動更改欄位,其他微服務只能被動更改(接收源頭發出的更改訊息之后再改),在本例子中, “quantity”和“price”欄位的源頭是右邊的表,其他的欄位的源頭都是左邊的表,本例子中“quantity”和“price”只在從表中存在,因此資料寫入是單向的,方向是主表到從表,如果主表也需要這些欄位,那么它們還要被回寫,那資料寫入就變成雙向的,

直接訪問其它資料庫:

這種方式是要絕對禁止的,生產環境中的許多程式錯誤和性能問題都是由這種方式產生的,上面的三種方式由于是另外新建了本地只讀資料庫表,產生了資料庫的物理隔離,這樣一個資料庫的性能問題不會影響到另一個,另外,當主庫中的表結構更改時,你可以暫時保持從庫中的表不變,這樣程式還可以運行,如果直接訪問別人的庫,主庫一修改,別的微服務程式馬上就會報錯,請參閱ApplicationDatabase,

向后兼容的資料庫更新:

從上面的論述可以看出,資料庫表結構的修改是一個影響范圍很廣的事情,在微服務架構中,共享的表在別的服務中也會有一個只讀的拷貝,現在當你要更改表結構時,還需要考慮到對別的微服務的影響,當在單體(Monolithic)架構中,為了保證程式部署能夠回滾,資料庫的更新是向后兼容的,需要兼容性的另一個原因是支持藍綠發布(Blue-Green Deployment),在這種部署方式中,你同時擁有新舊版本的代碼,由負載均衡來決定每一個請求指向那個版本,它們可以共享一個資料庫(這就要求資料庫是向后兼容的),也可以使用不同的資料,資料庫的更新簡單來講有以下幾種型別:

  • 增加表或欄位:如果欄位可取空值,這個操作是向后兼容的,如果是非空值就要插入一個預設值,
  • 洗掉表或欄位:可先暫時保留被洗掉表或欄位,經過幾個版本之后再洗掉,
  • 修改欄位名:新增加一個欄位,把資料從舊欄位拷貝到新欄位,用資料庫觸發器(或程式)同步舊欄位和新欄位(供過渡時期使用), 然后再在幾個版本之后把原來的欄位洗掉(請參閱Update your Database Schema Without Downtime),
  • 修改表名:如果資料庫支持可更新視圖,最簡單的辦法是先修改表的名字,然后創建一個可更新視圖指向原來的表(請參閱Evolutionary Database Design ),如果資料庫不支持可更新視圖,使用的方法與修改欄位名相似,需要創建新的表并做資料同步,
  • 修改欄位型別:與修改欄位名幾乎相同,只是在拷貝資料時,需要做資料型別轉換,

向后兼容的資料庫更新的好處是,當程式部署出現問題時,如需進行回滾,只要回滾程式就行了,而不必回滾資料庫,回滾時一般只回滾一個版本,凡是需要洗掉的表或欄位在本次部署時都不做修改,等到一個或幾個版本之后,確認沒有問題了再洗掉,它的另一個好處就是不會對其他微服務中的共享表產生立刻的直接影響,當本微服務升級后,其他微服務可以評估這些資料庫更新帶來的影響再決定是否需要做相應的程式或資料庫修改,

跨服務事物:

微服務的一個難點是如何實作跨服務的事物支持,兩階段提交(Two-Phase Commit)已被證明性能上不能滿足需求,現在基本上沒有人用,被一致認可的方法叫Saga,它的原理是為事物中的每個操作寫一個補償操作(Compensating Transaction),然后在回滾階段挨個執行每一個補償操作,示例如下圖,在一個事物中共有3個操作T1,T2,T3,每一個操作要定義一個補償操作,C1,C2,C3,事物執行時是按照正向順序先執行T1,當回滾時是按照反向順序先執行C3, 事物中的每一個操作(正向操作和補償操作)都被包裝成一個命令(Command),Saga執行協調器(Saga Execution Coordinator (SEC))負責執行所有命令,在執行之前,所有的命令都會按順序被存入日志中,然后Saga執行協調器從日志中取出命令,依次執行,當某個執行出現錯誤時,這個錯誤也被寫入日志,并且所有正在執行的命令被停止,開始回滾操作,

file

圖片來源

Saga放松了對一致性(Consistency)的要求,它能保證的是最終一致性(Eventual Consistency),因此在事物執行程序中資料是不一致的,并且這種不一致會被別的行程看到,在生活中,大多數情況下,我們對一致性的要求并沒有那么高,短暫的不一致性是可以接收的,例如銀行的轉賬操作,它們在執行程序中都不是在一個資料庫事物里執行的,而是用記賬的方式分成兩個動作來執行,保證的也是最終一致性,

Saga的原理看起來很簡單,但要想正確的實施還是有一定難度的,它的核心問題在于對錯誤的處理,要把它完全講明白需要另寫一遍文章,我現在只講一下要點,網路環境是不可靠的,正在執行的命令可能很長時間都沒有回傳結果,這時,第一,你要設定一個超時,第二,因為你不知道沒有回傳值的原因是,已經完成了命令但網路出了問題,還是沒完成就犧牲了,因此不知道是否要執行補償操作,這時正確的做法是重試原命令,直到得到完成確認,然后再執行補償操作,但這對命令有一個要求,那就是這個操作必須是冪等的(Idempotent),也就是說它可以執行多次,但最終結果還是一樣的,

另外,有些操作的補償操作比較容易生成,例如付款操作,你只要把錢款退回就可以了,但有些操作,像發郵件,完成之后就沒有辦法回到之前的狀態了,這時就只能再發一個郵件更正以前的資訊,因此補償操作不一定非要回傳到原來的狀態,而是抵消掉原來操作產生的效果,如果想了解更多,請看這里.

微服務的拆分:

我們原來的程式大多數都是單體程式,但現在要把它拆分成微服務,應該怎樣做才能降低對現有應用的影響呢?

file

圖片來源

我們用上面的圖來做例子,它共有兩個程式,一個是“Styling app”,另一個是“Warehouse app”,它們共享圖中下面的資料庫,庫里有三張表,“core client”,“core sku”,“core item”,

file

圖片來源

假設我們要拆分出來一個微服務叫“client-service”,它需要訪問“core client”表,第一步,我們先把程式從原來的代碼里拆分出來,變成一個服務. 資料庫不動,這個服務仍然指向原來的資料庫,其他程式不再直接訪問這個服務管理的表,而是通過服務呼叫或另建共享表來獲取資料,

file

圖片來源

第二步,再把服務的資料庫表拆分出來,這時微服務就擁有它自己的資料庫了,而不再需要原來的共享資料庫了,這時就成了一個真正意義上的的微服務,

上面只講了拆分一個微服務,如果有多個需要拆分,則需一個一個按照上面講的方法依次進行,

另外,Martin Fowler在他的文章"Break Monolith into Microservices"里有一個很好的建議,那就是,當你把服務從單體程式里拆分時,不要只想著把代碼拆分出來,因為現在的需求可能已經跟原來有所不同,原先的設計可能也不太適用了,而且,技術也已更新,代碼也要作相應的改造,更好的辦法是重寫原來的功能(而不是重寫原來的代碼),把重點放在拆分業務功能上,而不是拆分代碼上,用新的設計和技術來實作這個業務功能,

結論:

資料庫設計是微服務設計的一個關鍵點,基本原則是每個微服務都有自己單獨的資料庫,而且只有微服務本身可以訪問這個資料庫,微服務之間的資料共享可以通過服務呼叫,或者主、從表的方式實作,在共享資料時,要找到合適的同步方式,在微服務架構中,資料庫的修改影響廣泛,需要保證這種修改是向后兼容的,實作跨服務事物的標準方法是Saga,當把單體程式拆分成微服務時,可以分步進行,以減少對現有程式的影響,

索引:

  1. 微服務之間呼叫的最佳設計
  2. ApplicationDatabase
  3. Update your Database Schema Without Downtime
  4. Evolutionary Database Design
  5. Fault-Tolerance and Data Consistency Using Distributed Sagas
  6. Distributed Sagas: A Protocol for Coordinating Microservices - Caitie McCaffrey - JOTB17
  7. Managing Data in Microservices
  8. How to break a Monolith into Microservices

本文由博客一文多發平臺 OpenWrite 發布!

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/44301.html

標籤:架構設計

上一篇:學習重構(1)-代碼的壞味道

下一篇:大型網站多級快取的分層架構

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 面試突擊第一季,第二季,第三季

    第一季必考 https://www.bilibili.com/video/BV1FE411y79Y?from=search&seid=15921726601957489746 第二季分布式 https://www.bilibili.com/video/BV13f4y127ee/?spm_id_fro ......

    uj5u.com 2020-09-10 05:35:24 more
  • 第三單元作業總結

    1.前言 這應該是本學期最后一次寫作業總結了吧。總體來說,對作業的節奏也差不多掌握了,作業做起來的效率也更高了。雖然和之前的作業一樣,作業中都要用到新的知識,但是相比之前,更加懂得了如何利用工具以及資料。雖然之間卡過殼,但總體而言,這幾次作業還算完成的比較好。 2.作業程序總結 相比前兩個單元,此單 ......

    uj5u.com 2020-09-10 05:35:41 more
  • 北航OO(2020)第四單元博客作業暨課程總結博客

    北航OO(2020)第四單元博客作業暨課程總結博客 本單元作業的架構設計 在本單元中,由于UML圖具有比較清晰的樹形結構,因此我對其中需要進行查詢操作的元素進行了包裝,在樹的父節點中存盤所有孩子的參考。考慮到性能問題,我采用了快取機制,一次查詢后盡可能快取已經遍歷過的資訊,以減少遍歷次數。 本單元我 ......

    uj5u.com 2020-09-10 05:35:48 more
  • BUAA_OO_第四單元

    一、UML決議器設計 ? 先看下題目:第四單元實作一個基于JDK 8帶有效性檢查的UML(Unified Modeling Language)類圖,順序圖,狀態圖分析器 MyUmlInteraction,實際上我們要建立一個有向圖模型,UML中的物件(元素)可能與同級元素連接,也可與低級元素相連形成 ......

    uj5u.com 2020-09-10 05:35:54 more
  • 6.1邏輯運算子

    邏輯運算子 1. && 短路與 運算式1 && 運算式2 01.運算式1為true并且運算式2也為true 整體回傳為true 02.運算式1為false,將不會執行運算式2 整體回傳為false 03.只要有一個運算式為false 整體回傳為false 2. || 短路或 運算式1 || 運算式2 ......

    uj5u.com 2020-09-10 05:35:56 more
  • BUAAOO 第四單元 & 課程總結

    1. 第四單元:StarUml檔案決議 本單元采用了圖模型決議UML。 UML檔案可以抽象為圖、子圖、邊的邏輯結構。 在實作中,圖的節點包括類、介面、屬性,子圖包括狀態圖、順序圖等。 采用了三次遍歷UML元素的方法建圖,第一遍遍歷建點,第二、三次遍歷設定屬性、連邊,實作圖物件的初始化。這里借鑒了一些 ......

    uj5u.com 2020-09-10 05:36:06 more
  • 談談我對C# 多型的理解

    面向物件三要素:封裝、繼承、多型。 封裝和繼承,這兩個比較好理解,但要理解多型的話,可就稍微有點難度了。今天,我們就來講講多型的理解。 我們應該經常會看到面試題目:請談談對多型的理解。 其實呢,多型非常簡單,就一句話:呼叫同一種方法產生了不同的結果。 具體實作方式有三種。 一、多載 多載很簡單。 p ......

    uj5u.com 2020-09-10 05:36:09 more
  • Python 資料驅動工具:DDT

    背景 python 的unittest 沒有自帶資料驅動功能。 所以如果使用unittest,同時又想使用資料驅動,那么就可以使用DDT來完成。 DDT是 “Data-Driven Tests”的縮寫。 資料:http://ddt.readthedocs.io/en/latest/ 使用方法 dd. ......

    uj5u.com 2020-09-10 05:36:13 more
  • Python里面的xlrd模塊詳解

    那我就一下面積個問題對xlrd模塊進行學習一下: 1.什么是xlrd模塊? 2.為什么使用xlrd模塊? 3.怎樣使用xlrd模塊? 1.什么是xlrd模塊? ?python操作excel主要用到xlrd和xlwt這兩個庫,即xlrd是讀excel,xlwt是寫excel的庫。 今天就先來說一下xl ......

    uj5u.com 2020-09-10 05:36:28 more
  • 當我們創建HashMap時,底層到底做了什么?

    jdk1.7中的底層實作程序(底層基于陣列+鏈表) 在我們new HashMap()時,底層創建了默認長度為16的一維陣列Entry[ ] table。當我們呼叫map.put(key1,value1)方法向HashMap里添加資料的時候: 首先,呼叫key1所在類的hashCode()計算key1 ......

    uj5u.com 2020-09-10 05:36:38 more
最新发布
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:20:47 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:20:25 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:20:17 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:20:10 more
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:19:44 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:19:07 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:18:57 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:18:49 more
  • 05單件模式

    #經典的單件模式 public class Singleton { private static Singleton uniqueInstance; //一個靜態變數持有Singleton類的唯一實體。 // 其他有用的實體變數寫在這里 //構造器宣告為私有,只有Singleton可以實體化這個類! ......

    uj5u.com 2023-04-19 08:42:51 more
  • 【架構與設計】常見微服務分層架構的區別和落地實踐

    軟體工程的方方面面都遵循一個最基本的道理:沒有銀彈,架構分層模型更是如此,每一種都有各自優缺點,所以請根據不同的業務場景,并遵循簡單、可演進這兩個重要的架構原則選擇合適的架構分層模型即可。 ......

    uj5u.com 2023-04-19 08:42:41 more