面試官:為什么每個微服務都要有自己獨立的數(shù)據(jù)庫?
這個問題是我在知乎上看到的,答案并不是一邊倒,還是存在爭議性的。
其中,一些持反方觀點同學的理由如下:
(1)從硬件成本和維護成本上看,反而應該多個微服務盡可能地共用中間件和數(shù)據(jù)庫。
(2)微服務的獨立數(shù)據(jù)庫是指邏輯獨立,而不是物理獨立,在物理層面上是可以共用數(shù)據(jù)庫的。
btw:有點兒像《非誠勿擾》中葛優(yōu)對舒淇說的話:“那你能允許我心在你這,身體上開點兒小差嗎”?
(3)你把數(shù)據(jù)庫也看做一個微服務就好理解了,微服務之間本身就是多對多的關系,公用有何不可呢?
拋出我的觀點吧,我完全支持每個微服務都要有自己獨立的數(shù)據(jù)庫,但每個數(shù)據(jù)庫是否使用獨立的服務器,這個需要視業(yè)務情況而定。
具體原因請見下圖:
圖片
系統(tǒng)可用性
說說我當年的情況吧,當時我在一個在線教育公司,該公司早期的系統(tǒng)是一個單體架構,一個大的后端工程,并對應一個大的數(shù)據(jù)庫。
后來人越招越多,業(yè)務也越來越復雜,幾十人的研發(fā)團隊共同在一個大的單體服務中進行開發(fā),這是明顯不現(xiàn)實的。
于是,研發(fā)團隊便按照組織結構進行服務拆分,將那個大的單體服務拆分為學生端服務、教師端服務、管理端服務和銷售端服務。
圖片
隨后,我們的學生端服務又按照業(yè)務領域,拆分出來了學生課表、積分商城和學生運營活動服務。
其中,學生課表服務的重要等級是P0(最重要的),因為學生是以課表為入口進入教室上課的,也就是說,如果課表服務掛了就會導致無法上課,會給公司帶來重大經濟損失。
積分商城服務,是學生通過積分兌換學習用品的,如果服務掛了會影響用戶體驗,但不會造成經濟損失,因此重要等級為P1。
而運營活動服務的主要功能為,公司的運營人員會創(chuàng)建一些活動來增加學生在平臺上的活躍度,比如:學生將自己創(chuàng)作的作品,發(fā)到朋友圈中收集點贊,被點贊最多的學生獲得小禮品,等等。
但當時的情況是,學生課表、積分商城和學生運營活動這三個服務拆出來了,還是共用了一個學生端的數(shù)據(jù)庫。
有一天晚上,正好是學生上課的高峰期,忽然收到一通報警,學生端數(shù)據(jù)庫的負載竟然達到了200多,CPU使用率也被干到了100%。
緊接著,公司的大群中就有好幾個業(yè)務同事發(fā)消息說,學生投訴說課表看不到了,不能進入教室上課了。
我們聽了之后,趕緊登錄監(jiān)控系統(tǒng)去查看學生課表服務的相關接口,發(fā)現(xiàn)由于不顯示課表學生反復刷新頁面的原因,接口的QPS確實比正常情況下高了好幾倍,但都被Sentinel限流掉了,不應該造成影響才對。
正在一籌莫展之際,我忽然發(fā)現(xiàn)在監(jiān)控系統(tǒng)上,學生運營活動服務的接口QPS和TPS同樣高得離譜,比平時高十幾倍,且彪高的起始時間要比學生課表接口早一兩分鐘。
這就證明了,很大可能是學生運營活動服務的流量彪高,且學生運營活動服務上的接口沒有做限流保護,造成了服務共用的學生端數(shù)據(jù)庫扛不住了,從而影響了學生課表服務。
圖片
于是,我們趕緊啟動降級機制,關閉了所有的學生運營活動服務的接口。沒過一會兒,學生課表可以正常訪問了。
接下來,我們和DBA連夜把學生課表服務所對應的數(shù)據(jù)表單獨拆分出來,遷移到一個新的數(shù)據(jù)庫服務器上進行獨享。
嗯,重要等級高的微服務不但要有自己獨立的數(shù)據(jù)庫,且必須是獨立的數(shù)據(jù)庫服務器,通過鏈路隔離的方式提升系統(tǒng)可用性。
當然,一些重要等級不高的服務獨立數(shù)據(jù)庫即可,并不需要獨立數(shù)據(jù)庫服務器,這樣可以節(jié)省硬件成本。
架構貫徹性
我們都知道,微服務架構之間是通過RPC調用來進行業(yè)務串聯(lián)的。
以常見的電商場景舉例,需要給用戶展示他所購買的訂單列表,此時訂單中心會調用商品中心的API獲取商品數(shù)據(jù),然后再跟訂單數(shù)據(jù)進行merge,返回給前端用戶。
如果此時商品中心和訂單中心所對應的數(shù)據(jù)表放在一個數(shù)據(jù)庫中,可以預見的情況就是,研發(fā)人員會把訂單表和商品表進行多表關聯(lián)的方式來代替RPC調用+ 數(shù)據(jù)merge,因為這樣做非常省事。
如下圖所示:
而一旦破了這個口子,就會形成“破窗效應”,系統(tǒng)架構就變成分布式單體架構。
有人說,可以靠口頭約束的方式來規(guī)避這種情況,我并不認同。
想象一種場景,如果一個同學趕項目工期,半夜12點還在那挑燈夜戰(zhàn)呢。此時,如果有一種方式讓他快速寫完代碼回家睡覺,他會毫不動心一絲不茍地“按照規(guī)律辦事”?
研發(fā)效率
微服務獨立數(shù)據(jù)庫的另一個好處就是,讀寫入口收斂,這樣是可以提升研發(fā)效率的。
舉個例子,如果我們把商品表進行垂直拆分,拆分成商品表 + 商品詳情表,如果按照標準的獨立數(shù)據(jù)庫方式,只需要商品中心來進行對應的代碼變更就可以了,這對依賴商品中心的其他服務來講是透明的。
而非獨立庫模式就比較蛋疼了,每個去直接查詢商品表的服務都需要改一遍。
如下圖所示:
圖片
上述例子屬于讀入口收斂的范疇,而寫入口不收斂,在多個服務中對一張數(shù)據(jù)表進行寫入的話,則帶來的問題同樣不少。
問題包括:
(1)表結構變更問題,如果增加一個非空字段,那么所有寫入口的代碼全部需要變更,且這種“散彈式”修改非常容易遺漏。
(2)問題排查難,一旦發(fā)現(xiàn)寫入了問題數(shù)據(jù),那各個寫入口全部需要進行排查,工作量大且復雜。
除了讀寫入口收斂問題,再有就是,如果形成了上文中所說的“分布式單體架構”,那接下來再想進行優(yōu)化改善的話,將會是一件工作量極大的事情,所以不如一次做好。
結語
綜上所述,我認為如果選擇了微服務架構,那每個微服務獨立數(shù)據(jù)庫完全是個必選項,獨立數(shù)據(jù)庫服務器則是個可選項,需要兼顧可用性和硬件成本。