讀服務+寫服務分離架構,我堅決反對!
系統(tǒng)分層架構有一個迭代和演進的過程,早期,系統(tǒng)二層架構如下:
(1)上游是業(yè)務應用;
(2)下游是數(shù)據(jù)庫;
隨著架構的演進,可能要抽取出微服務,系統(tǒng)三層架構如下:
(1)上游仍是業(yè)務應用;
(2)中間是微服務層,提供RPC接口;
(3)下游是數(shù)據(jù)庫;
大家都知道,數(shù)據(jù)庫可以讀寫分離,為了職責更清新,架構設計上,服務能否讀寫分離呢?
如上圖,服務化讀寫分離之后:
(1)業(yè)務方通過RPC分別調(diào)用讀服務和寫服務;
(2)服務層分為讀服務與寫服務;
(3)底層是高可用的數(shù)據(jù)庫集群;
當然,也有可能讀服務與寫服務讀寫的是不同的數(shù)據(jù)庫,如上圖:
(1)寫服務訪問寫庫;
(2)讀服務訪問讀庫;
寫庫與讀庫是一個主從同步的集群。
那么,問題來了:
(1)你遇到過這種讀服務+寫服務分離的架構設計么?
(2)這種架構設計好還是不好,為什么?
樓主支持這種讀寫服務分離的架構設計么?
先說結論,我旗幟鮮明的反對服務區(qū)分讀寫分離。
為什么反對呢?
大大小小的理由,有這么五點。
第一點:對于調(diào)用方而言,調(diào)用同一個基礎服務,要訪問其RPC接口,究竟調(diào)用讀服務,還是寫服務,容易困惑。
第二點:對于同一個基礎服務,服務數(shù)量翻倍了,運維更加復雜。
畫外音:總的來說,上面兩點還可以忍。
第三點:一般來說,服務拆分,是按照“子業(yè)務”維度進行拆分,而不是按照“讀寫”維度進行拆分,這是模塊化設計的基本準則。
畫外音:這一點,是原則性問題。
第四點:完全打破了“服務化數(shù)據(jù)庫私有”的微服務初衷。
畫外音:數(shù)據(jù)訪問,應該收口。
兩個服務因為同一份數(shù)據(jù)庫資源訪問而耦合在一起,當數(shù)據(jù)庫資源發(fā)生變化的時候(例如:ip變化,域名變化,表結構變化,水平切分變化等),有兩個依賴點需要修改。
而好的設計,有變化產(chǎn)生時,只有一個需要修改(低耦合,高內(nèi)聚)。
第五點:沒法很好的添加緩存。
畫外音:這一點很致命。
大部分互聯(lián)網(wǎng)業(yè)務是讀多寫少的業(yè)務,數(shù)據(jù)庫讀取最容易成為瓶頸,常見提升讀性能的方式是,增加緩存。
如上圖,讀服務的下游增加一個緩存,當有讀請求訪問時:
(1)先訪問緩存,如果命中,直接返回;
(2)如果緩存不命中,訪問數(shù)據(jù)庫,然后將數(shù)據(jù)放入緩存中,以便下一次能夠命中;
額,然后,這個架構中,這個方案是不可行的。因為,寫服務修改數(shù)據(jù)庫時,緩存中的數(shù)據(jù)沒有辦法得到淘汰!!!
OK,有朋友說,寫數(shù)據(jù)庫之前,可以由寫服務來淘汰緩存:
即,讀服務與寫服務都可以操作緩存。額,這個設計,又違背了“服務化緩存私有”的微服務初衷,兩個服務因為同一份緩存資源訪問而耦合在一起,當緩存資源發(fā)生變化的時候,有兩個依賴點需要修改。
畫外音:緩存訪問,應該收口。
況且,如果真的兩個服務訪問相同的數(shù)據(jù)庫和緩存,為什么不合成一個服務呢?
硬要拆成兩個服務,不是自己玩自己么?
OK,有另外的朋友說,可以由寫服務發(fā)消息來淘汰緩存:
如上圖:
(1)緩存私有,只有讀服務操縱緩存;
(2)數(shù)據(jù)庫發(fā)生寫請求時,寫服務給MQ發(fā)消息,由讀服務來淘汰緩存;
這種設計:
(1)讀服務來淘汰緩存,本質是一個寫請求,不是很奇怪么?
(2)引入了一個MQ組件,引入更大的一致性風險;
(3)讀服務和寫服務如果是一個進程,豈不是更好么,干嘛硬要跨進程通信呢?
所以,還是一個服務更好:
(1)調(diào)用方不蒙圈,不糾結;
(2)好維護;
(3)數(shù)據(jù)庫,緩存私有,無耦合;
總的來說,個人的意見是:
互聯(lián)網(wǎng)微服務架構,建議按照“子業(yè)務”進行微服務拆分,而不應該按照“讀寫”來進行微服務拆分,避免過度設計。
以上僅為個人架構經(jīng)驗,希望邏輯是清晰的,供大伙參考,歡迎共同探討。
【本文為51CTO專欄作者“58沈劍”原創(chuàng)稿件,轉載請聯(lián)系原作者】